././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.136956 cedar_backup3-3.8.1/Changelog0000644000000000000000000012523114567004737012772 0ustar00Version 3.8.1 26 Feb 2024 * Upgrade to Poetry v1.8.0 and make related adjustments. * Disable Python keyring via poetry.toml. * Update Poetry install instructions in DEVELOPER.md. * Pull in latest version of run-script-framework. * Upgrade to gha-shared-workflows@v4 with new Poetry setup. * Add support for 'run outdated' and update dependencies. Version 3.8.0 17 Jan 2024 * Constrain some dependencies to be compatible with Debian. * Disable intersphinx plugin for docs, which no longer seems to work. * Update jinja2 to address CVE-2024-22195 from Dependabot. Version 3.7.9 05 Nov 2023 * Update urllib3 to address Dependabot warnings. * Upgrade to Poetry v1.7.0 for official Python 3.12 support. Version 3.7.8 15 Oct 2023 * Fix Python 3.12 deprecation for datetime.datetime.utcnow(). * Fix Python 3.12 warning for tar.extract() filter behavior. * Fix some non-working remote connectivity tests in 'run tests full'. Version 3.7.7 14 Oct 2023 * Upgrade major dependencies. * Upgrade build process to Poetry v1.6.1. * Add support for Python v3.12 and drop support for v3.8. * Document changes to scp configuration for Debian bookworm. Version 3.7.6 10 Jun 2023 * Pin chardet and importlib-metadata to match Debian bookworm. Version 3.7.5 07 Jun 2023 * Fix checktabs to be safe for file named '-'. * Upgrade all dependencies to the latest major version. * Upgrade to Poetry v1.5.1 and make related adjustments. Version 3.7.4 31 Dec 2022 * Pull Sphinx fields from metadata rather than parsing pyproject.toml. Version 3.7.3 30 Dec 2022 * Convert to latest readthedocs.io standard. * Update remaining docs, comments, and checks to reflect dropping Python 3.7. * Automate the existing manual release process using GitHub Actions. Version 3.7.2 30 Dec 2022 * More improvements to metadata for the Debian CI test process. Version 3.7.1 30 Dec 2022 * Improvements to metadata to better support the Debian build process. Version 3.7.0 29 Dec 2022 * Drop support for Python 3.7. * Rename CREDITS -> NOTICE for consistency with my other Python projects. * Migrate release.py and various help output to use importlib.metadata. * Convert to standard version of .run/tasks/release.sh used elsewhere. Version 3.6.9 09 Nov 2022 * Support Python 3.11. * Use locale.getlocale() instead of .getdefaultlocale() for Python >= 3.11. * Adjust GHA build to run MacOS and Windows tests only on latest Python. Version 3.6.8 24 Oct 2022 * Remove execute bit from amazons3.py and span.py for Debian policy. Version 3.6.7 24 Oct 2022 * Refactor the run script and pre-commit hooks to reduce duplication. * Rework the GitHub Actions build process to pull out shared code. Version 3.6.6 05 Sep 2022 * Upgrade to Poetry v1.2.0 and make related build process changes. * Upgrade to Pylint v2.15.0 and address warnings. * Fix Pylint's configured class-attribute-naming-style to be snake_case. * Adjust Black configuration to target Python 3.7, 3.8, 3.9, and 3.10. * Add dependency caching to GitHub Actions workflow. * Fix the GitHub Actions matrix build to properly target Windows and MacOS. * Fix problems with test suite under Windows when running in GitHub Actions. * Update various dependencies for the developer environment. Version 3.6.5 30 Apr 2022 * Adjust the pyproject.toml include directive to limit what goes into wheel. * Remove the Safety scanner from the pre-commit hooks and GitHub action. Version 3.6.4 14 Nov 2021 * Adjust dependencies and build process to support Python 3.10. Version 3.6.3 13 Mar 2021 * Improve logged output for failed pre/post action hooks. Version 3.6.2 17 Feb 2021 * Standardize format of GPL license statement in CREDITS. Version 3.6.1 07 Feb 2021 * Make minor structural improvements to the build process. * Publish coverage data to coveralls.io in GitHub workflow. Version 3.6.0 03 Feb 2021 * Always use '/' internally for paths, even on the Windows platform. * Change the way archive filenames are generated to eliminate leading '-' characters. * Use UTF-8 encoding for log files on disk to avoid problems with unusual filenames. * Update build process to include security scans using Safety. Version 3.5.4 17 Jan 2021 * Fix a couple of links in docs/index.rst (cut-and-paste error). Version 3.5.3 17 Jan 2021 * Move to newer build dependencies (Black, isort, etc.). * Make adjustments to code formatting to match new Black version. * Force docs/requirements.txt to have UNIX line endings * Convert to PEP 440 convention for Git tags (i.e. "v3.5.3"). * Standardize and improve badges in markdown, so they are all links. * Simplify pylint configuration and use "-j 0" to speed it up. Version 3.5.2 14 Jan 2021 * Improve debug output for pre/post action hooks. Version 3.5.1 30 Dec 2020 * Adjust .gitattributes to force UNIX line endings, for compatibility. Version 3.5.0 30 Dec 2020 * Changes to support the Windows platform, for the first time since 3.0.1. * Minor tweaks to the codebase to make the PyCharm code inspector happy. Version 3.4.1 20 Dec 2020 * Fix Python dependencies to work with Python 3.9.1 and not just 3.9.0. Version 3.4.0 07 Nov 2020 * Add a new -u/--uploadOnly option to cback3-amazons3-sync. Version 3.3.4 29 Oct 2020 * Update manual's copyright to reflect reStructuredText, not Docbook XML. * Fix typos (misspellings) discovered in manpages by Debian's Lintian. * Fix bug in "run release" that resulted in nonsensical 3.3.3 release. Version 3.3.2 26 Oct 2020 * Fix obsolete GitHub URL in several places. Version 3.3.1 25 Oct 2020 * Minor adjustments to PyPI package documentation. * Execute 'sudo -v' prior to running full test suite. * Fix release process to work consistently across platforms. Version 3.3.0 25 Oct 2020 * Convert to modern Python packaging based on Poetry. * Apply code formatting standards with Black and isort. * Switch to standard unittest discovery process to run test suite. * Convert user manual from Docbook XML to Sphinx and revise content. * Migrate all documentation (manual and API specs) to Read the Docs. * Add a GitHub workflow to implement code quality gates, executed via Tox. * Clean up and remove obsolete and unused notes, utilities, etc. * Fix resource leak in executeCommand() that was exposed by unit tests. * Increase minimum interpreter version from Python 3.4 to Python 3.7. Version 3.2.0 24 Aug 2019 * Migrate to GitHub now that BitBucket no longer supports Mercurial repositories. * Handle new warnings from PyLint 2.3.1. Version 3.1.12 12 Nov 2017 * Fix MANIFEST.in so that all API documentation is included in tarball. Version 3.1.11 12 Nov 2017 * Convert API documentation from Epydoc to Sphinx Napolean (Google docstrings). Version 3.1.10 11 Sep 2017 * Fix maintainer script util/validate to use onsgmls from the opensp package. * Add information to the user manual about how to lock down the Amazon S3 user. Version 3.1.9 03 Jul 2017 * Fix issues discovered with peer test changes when backporting to CedarBackup2. Version 3.1.8 02 Jul 2017 * Add options in util/test.py to get better output from unit tests. * Update Python virtualenv setup to work with Python 3.5 and recent pip. * Make various adjustments to remote peer tests, to make them safer and more robust. * Fix problems discovered with the awscli version in Debian stretch. Version 3.1.7 21 Jun 2016 * Fix typo in cback.1, noticed by Debian's Lintian checks. * Fix logging errors with odd paths in tools/amazons3.py when ascii encoding is set. Version 3.1.6 13 Feb 2016 * Fix bug in the file encoding checks for the amazons3sync tool. Version 3.1.5 02 Jan 2016 * Fix or disable a variety of new warnings and suggestions from pylint. Version 3.1.4 11 Aug 2015 * Improvements based on testing in the Debian continuous integration environment. - Make the logging setup process obey the --stack command-line option - Fix logging setup to always create the log file with the proper specified mode - Fix PurgeItemList.removeYoungFiles() so ageInWholeDays can never be negative - Make filesystemtests more portable, with maximum file path always <= 255 bytes Version 3.1.1 04 Aug 2015 * Fix incorrect exception raise without % in util.py, found by accident. * Fix cut-and-paste typo 'iplemented' in cli.py, config.py, and util.py. * Fix bugs in the ByteQuantity changes from v3.1.0, so comparisons work properly. * Adjust amazons3, capacity and split to use ByteQuantity directly, not bytes field. Version 3.1.0 03 Aug 2015 * Enhance ByteQuantity so it can be built from and compared to simple numeric values. * Improve the way the amazons3 extension deals with byte quantities. - Fix configuration to support quantities like "2.5 GB", as in other extensions - Improve logging using displayBytes(), so displayed quantities are more legible Version 3.0.2 30 Jul 2015 * Improvements based on integration testing with my own backup configuration. - Fix problems with pickle by using binary open, protocol=0, fix_imports=True - Be stricter with the way files are opened/closed, relying on the new 'with' idiom - Make sure every file opened for use with executeCommand() uses mode "wb" Version 3.0.1 29 Jul 2015 * Create project in Mercurial at BitBucket, alongside Cedar Backup 2. * Convert to Python 3, using v2.24.2 as the basis for conversion. * Rename files (logs, config, executables) to use cback3 prefix rather than cback. * Remove support for Windows and Cygwin, which was never advertised and rarely tested. * Fix a variety of minor warnings from pylint 1.4.4, most of which also appeared in 2.24.2. * Clean up manpages and add notes about migrating to version 3. * Review user guide, fix broken links, make minor tweaks to wording, etc. * Fix long-standing bugs with pre- and post-action hooks, ported from v2.24.4. Version 2.24.2 05 Jan 2015 * Add optional size-limit configuration for amazons3 extension. Version 2.24.1 07 Oct 2014 * Implement a new tool called cback-amazons3-sync. * Add support for missing --diagnostics flag in cback-span script. Version 2.23.3 03 Oct 2014 * Add new extension amazons3 as an optional replacement for the store action. * Update user manual and INSTALL to clarify a few of the dependencies. * Fix encryption unit test that started failing due to my new GPG key. Version 2.22.0 09 May 2013 * Add eject-related kludges to work around observed behavior. * New config option eject_delay, to slow down open/close * Unlock tray with 'eject -i off' to handle potential problems Version 2.21.1 21 Mar 2013 * Apply patches provided by Jan Medlock as Debian bugs. * Fix typo in manpage (showed -s instead of -D) * Support output from latest /usr/bin/split (' vs. `) Version 2.21.0 12 Oct 2011 * Update CREDITS file to consistently credit all contributers. * Minor tweaks based on PyLint analysis (mostly config changes). * Make ISO image unit tests more robust in writersutiltests.py. - Handle failures with unmount (wait 1 second and try again) - Programmatically disable (and re-enable) the GNOME auto-mounter * Implement configurable recursion for collect action. - Update collect.py to handle recursion (patch by Zoran Bosnjak) - Add new configuration item CollectDir.recursionLevel - Update user manual to discuss new functionality Version 2.20.1 19 Oct 2010 * Fix minor formatting issues in manpages, pointed out by Debian lintian. * Changes required to make code compatible with Python 2.7 - StreamHandler no longer accepts strm= argument (closes: #3079930) - Modify logfile os.fdopen() to be explicit about read/write mode - Fix tests that extract a tarfile twice (exposed by new error behavior) Version 2.20.0 07 Jul 2010 * This is a cleanup release with no functional changes. * Switch to minimum Python version of 2.5 (everyone should have it now). - Make cback script more robust in the case of a bad interpreter version - Change file headers, comments, manual, etc. to reference Python 2.5 - Convert to use @staticmethod rather than x = staticmethod(x) - Change interpreter checks in test.py, cli.py and span.py - Remove Python 2.3-compatible versions of util.nullDevice() and util.Pipe * Configure pylint and execute it against the entire codebase. - Fix a variety of minor warnings and suggestions from pylint - Move unit tests into testcase folder to avoid test.py naming conflict * Remove "Translate [x:y] into [a:b]" debug message for uid/gid translation. * Refactor out util.isRunningAsRoot() to replace scattered os.getuid() calls. * Remove boilerplate comments "As with all of the ... " in config code. * Refactor checkUnique() and parseCommaSeparatedString() from config to util. * Add note in manual about intermittent problems with DVD writer soft links. Version 2.19.6 22 May 2010 * Work around strange stderr file descriptor bugs discovered on Cygwin. * Tweak expected results for tests that fail on Cygwin with Python 2.5.x. * Set up command overrides properly so full test suite works on Debian. * Add refresh_media_delay configuration option and related functionality. Version 2.19.5 10 Jan 2010 * Add customization support, so Debian can use wodim and genisoimage. * SF bug #2929447 - fix cback-span to only ask for media when needed * SF bug #2929446 - add retry logic for writes in cback-span Version 2.19.4 16 Aug 2009 * Add support for the Python 2.6 interpreter. - Use hashlib instead of deprecated sha module when available - Use set type rather than deprecated sets.Set when available - Use tarfile.format rather than deprecated tarfile.posix when available - Fix testGenerateTarfile_002() so expectations match Python 2.6 results Version 2.19.3 29 Mar 2009 * Fix minor epydoc typos, mostly in @sort directives. * Removed support for user manual PDF format (see doc/pdf). Version 2.19.2 08 Dec 2008 * Fix cback-span problem when writing store indicators. Version 2.19.1 15 Nov 2008 * Fix bug when logging strange filenames. Version 2.19.0 05 Oct 2008 * Fix a few typos in the CREDITS file. * Update README to properly reference SourceForge site. * Add option to peer configuration. Version 2.18.0 05 May 2008 * Add the ability to dereference links when following them. - Add util.dereferenceLink() function - Add dereference flag to FilesystemList.addDirContents() - Add CollectDir.dereference attribute - Modify collect action to obey CollectDir.dereference - Update user manual to discuss new attribute Version 2.17.1 26 Apr 2008 * Updated copyright statement slightly. * Updated user manual. - Brought copyright notices up-to-date - Fixed various URLs that didn't reference SourceForge * Fixed problem with link_depth (closes: #1930729). - Can't add links directly, they're implicitly added later by tar - Changed FilesystemList to use includePath=false for recursive links Version 2.17.0 20 Mar 2008 * Change suggested execution index for Capacity extension in manual. * Provide support for application-wide diagnostic reporting. - Add util.Diagnostics class to encapsulate information - Log diagnostics when Cedar Backup first starts - Print diagnostics when running unit tests - Add a new --diagnostics command-line option * Clean up filesystem code that deals with file age, and improve unit tests. - Some platforms apparently cannot set file ages precisely - Change calculateFileAge() to use floats throughout, which is safer - Change removeYoungFiles() to explicitly check on whole days - Put a 1-second fudge factor into unit tests when setting file ages * Fix some unit test failures discovered on Windows XP. - Fix utiltests.TestFunctions.testNullDevice_001() - Fix filesystemtests.TestBackupFileList.testGenerateFitted_004() - Fix typo in filesystemtests.TestFilesystemList.testRemoveLinks_002() Version 2.16.0 18 Mar 2008 * Make name attribute optional in RemotePeer constructor. * Add support for collecting soft links (closes: #1854631). - Add linkDepth parameter to FilesystemList.addDirContents() - Add CollectDir.linkDepth attribute - Modify collect action to obey CollectDir.linkDepth - Update user manual to discuss new attribute - Document "link farm" option for collect configuration * Implement a capacity-checking extension (closes: #1915496). - Add new extension in CedarBackup2/extend/capacity.py - Refactor ByteQuantity out of split.py and into config.py - Add total capacity and utilization to MediaCapacity classes - Update user manual to discuss new extension Version 2.15.3 16 Mar 2008 * Fix testEncodePath_009() to be aware of "UTF-8" encoding. * Fix typos in the PostgreSQL extension section of the manual. * Improve logging when stage action fails (closes: #1854635). * Fix stage action so it works for local users (closes: #1854634). Version 2.15.2 07 Feb 2008 * Updated copyright statements now that code changed in year 2008. * Fix two unit test failures when using Python 2.5 (SF #1861878). - Add new function testtutil.hexFloatLiteralAllowed() - Fix splittests.TestByteQuantity.testConstructor_004() for 0xAC - Fix configtests.TestBlankBehavior.testConstructor_006() for 0xAC Version 2.15.1 19 Dec 2007 * Improve error reporting for managed client action failures. * Make sure that managed client failure does not kill entire backup. * Add appendix "Securing Password-less SSH Connection" to user manual. Version 2.15.0 18 Dec 2007 * Minor documentation tweaks discovered during 3.0 development. * Add support for a new managed backup feature. - Add a new configuration section (PeersConfig) - Change peers configuration in to just override - Modify stage process to take peers list from peers section (if available) - Add new configuration in options and remote peers to support remote shells - Update user manual to discuss managed backup concept and configuration - Add executeRemoteCommand() and executeManagedAction() on peer.RemotePeer Version 2.14.0 19 Sep 2007 * Deal properly with programs that localize their output. - Create new util.sanitizeEnvironment() function to set $LANG=C - Call new sanitizeEnvironment() function inside util.executeCommand() - Change extend/split._splitFile() to be more verbose about problems - Update Extension Architecture Interface to mandate $LANG=C - Add split unit tests to catch any locale-related regressions - Thanks to Lukasz Nowak for initial debugging in split extension Version 2.13.2 10 Jul 2007 * Tweak some docstring markup to work with Epydoc beta 1. * Apply documentation patch from Lukasz K. Nowak. - Document that mysql extension can back up remote databases - Fix typos in extend/sysinfo.py * Clean up some configuration error messages to be clearer. - Make sure that reported errors always include enough information - Add a prefix argument to some of the specialized lists in util.py * Catch invalid regular expressions in config and filesystem code. - Add new util.RegexList list to contain only valid regexes - Use RegexList in config.ConfigDir and config.CollectConfig - Use RegexList in subversion.RepositoryDir and mbox.MboxDir - Throw ValueError on bad regex in FilesystemList remove() methods - Use RegexList in FilesystemList for all lists of patterns Version 2.13.1 29 Mar 2007 * Fix ongoing problems re-initializing previously-written DVDs - Even with -Z, growisofs sometimes wouldn't overwrite DVDs - It turns out that this ONLY happens from cron, not from a terminal - The solution is to use the undocumented option -use-the-force-luke=tty - Also corrected dvdwriter to use option "-dry-run" not "--dry-run" Version 2.13.0 25 Mar 2007 * Change writeIndicator() to raise exception on failure (closes #53). * Change buildNormalizedPath() for leading "." so files won't be hidden * Remove bogus usage of tempfile.NamedTemporaryFile in remote peer. * Refactored some common action code into CedarBackup2.actions.util. * Add unit tests for a variety of basic utility functions (closes: #45). - Error-handling was improved in some utility methods - Fundamentally, behavior should be unchanged * Reimplement DVD capacity calculation (initial code from Dmitry Rutsky). - This is now done using a growisofs dry run, without -Z - The old dvd+rw-mediainfo method was unreliable on some systems - Error-handling behavior on CdWriter was also tweaked for consistency * Add code to check media before writing to it (closes: #5). - Create new check_media store configuration option - Implement new initialize action to initialize rewritable media - Media is initialized by writing an initial session with media label - The store action now always writes a media label as well - Update user manual to discuss the new behavior - Add unit tests for new configuration * Implement an optimized media blanking strategy (closes: #48). - When used, Cedar Backup will only blank media when it runs out of space - Initial implementation and manual text provided by Dmitry Rutsky - Add new blanking_behavior store configuration options - Update user manual to document options and discuss usage - Add unit tests for new configuration Version 2.12.1 26 Feb 2007 * Fix typo in new split section in the user manual. * Fix incorrect call to new writeIndicatorFile() function in stage action. * Add notes in manual on how to find gpg and split commands. Version 2.12.0 23 Feb 2007 * Fix some encrypt unit tests related to config validation * Make util.PathResolverSingleton a new-style class (i.e. inherit from object) * Modify util.changeOwnership() to be a no-op for None user or group * Created new split extension to split large staged files. - Refactored common action utility code into actions/util.py. - Update standard actions, cback-span, and encrypt to use refactored code - Updated user manual to document the new extension and restore process. Version 2.11.0 21 Feb 2007 * Fix log message about SCSI id in writers/dvdwriter.py. * Remove TODO from public distribution (use Bugzilla instead). * Minor changes to mbox functionality (refactoring, test cleanup). * Fix bug in knapsack implementation, masked by poor test suite. * Fix filesystem unit tests that had typos in them and wouldn't work * Reorg user manual to move command-line tools to own chapter (closes: #33) * Add validation for duplicate peer and extension names (closes: #37, #38). * Implement new cback-span command-line tool (closes: #51). - Create new util/cback-span script and CedarBackup2.tools package - Implement guts of script in CedarBackup2/tools/span.py - Add new BackupFileList.generateSpan() method and tests - Refactor other util and filesystem code to make things work - Add new section in user manual to discuss new command * Rework validation requiring least one item to collect (closes: #34). - This is no longer a validation error at the configuration level - Instead, the collect action itself will enforce the rule when it is run * Support a flag in store configuration (closes: #39). - Change StoreConfig, CdWriter and DvdWriter to accept new flag - Update user manual to document new flag, along with warnings about it * Support repository directories in Subversion extension (closes: #46). - Add configuration modeled after - Make configuration value optional and for reference only - Refactor code and deprecate BDBRepository and FSFSRepository - Update user manual to reflect new functionality Version 2.10.1 30 Jan 2007 * Fix a few places that still referred only to CD/CD-RW. * Fix typo in definition of actions.constants.DIGEST_EXTENSION. Version 2.10.0 30 Jan 2007 * Add support for DVD writers and DVD+R/DVD+RW media. - Create new writers.dvdwriter module and DvdWriter class - Support 'dvdwriter' device type, and 'dvd+r' and 'dvd+rw' media types - Rework user manual to properly discuss both CDs and DVDs * Support encrypted staging directories (closes: #33). - Create new 'encrypt' extension and associated unit tests - Document new extension in user manual * Support new action ordering mechanism for extensions. - Extensions can now specify dependencies rather than indexes - Rewrote cli._ActionSet class to use DirectedGraph for dependencies - This functionality is not yet "official"; that will happen later * Refactor and clean up code that implements standard actions. - Split action.py into various other files in the actions package - Move a few of the more generic utility functions into util.py - Preserve public interface via imports in otherwise empty action.py - Change various files to import from the new module locations * Revise and simplify the implied "image writer" interface in CdWriter. - Add the new initializeImage() and addImageEntry() methods - Interface is now initializeImage(), addImageEntry() and writeImage() - Rework actions.store.writeImage() to use new writer interface * Refactor CD writer functionality and clean up code. - Create new writers package to hold all image writers - Move image.py into writers/util.py package - Move most of writer.py into writers/cdwriter.py - Move writer.py validate functions into writers/util.py - Move writertests.py into cdwritertests.py - Move imagetests.py into writersutiltests.py - Preserve public interface via imports in otherwise empty files - Change various files to import from the new module locations * More general code cleanup and minor enhancements. - Modify util/test.py to accept named tests on command line - Fix rebuild action to look at store config instead of stage. - Clean up xmlutil imports in mbox and subversion extensions - Copy Mac OS X (darwin) errors from store action into rebuild action - Check arguments to validateScsiId better (no None path allowed now) - Rename variables in config.py to be more consistent with each other - Add new excludeBasenamePatterns flag to FilesystemList - Add new addSelf flag to FilesystemList.addDirContents() - Create new RegexMatchList class in util.py, and add tests - Create new DirectedGraph class in util.py, and add tests - Create new sortDict() function in util.py, and add tests * Create unit tests for functionality that was not explictly tested before. - ActionHook, PreActionHook, PostActionHook, CommandOverride (config.py) - AbsolutePathList, ObjectTypeList, RestrictedContentList (util.py) Version 2.9.0 18 Dec 2006 * Change mbox extension to use ISO-8601 date format when calling grepmail. * Fix error-handling in generateTarfile() when target dir is missing. * Tweak pycheckrc to find fewer expected errors (from standard library). * Fix Debian bug #403546 by supporting more CD writer configurations. - Be looser with SCSI "methods" allowed in valid SCSI id (update regex) - Make config section's parameter optional - Change CdWriter to support "hardware id" as either SCSI id or device - Implement cdrecord commands in terms of hardware id instead of SCSI id - Add documentation in writer.py to discuss how we talk to hardware - Rework user manual's discussion of how to configure SCSI devices * Update Cedar Backup user manual. - Re-order setup procedures to modify cron at end (Debian #403662) - Fix minor typos and misspellings (Debian #403448 among others) - Add discussion about proper ordering of extension actions Version 2.8.1 04 Sep 2006 * Changes to fix, update and properly build Cedar Backup manual - Change DocBook XSL configuration to use "current" stylesheet - Tweak manual-generation rules to work around XSL toolchain issues - Document where to find grepmail utility in Appendix B - Create missing documentation for mbox exclusions configuration - Bumped copyright dates to show "(c) 2005-2006" where needed - Made minor changes to some sections based on proofreading Version 2.8.0 24 Jun 2006 * Remove outdated comment in xmlutil.py about dependency on PyXML. * Tweak wording in doc/docbook.txt to make it clearer. * Consistently rework "project description" everywhere. * Fix some simple typos in various comments and documentation. * Added recursive flag (default True) to FilesystemList.addDirContents(). * Added flat flag (default False) to BackupFileList.generateTarfile(). * Created mbox extension in CedarBackup2.extend.mbox (closes: #31). - Updated user manual to document the new extension and restore process. * Added PostgreSQL extension in CedarBackup2.extend.postgresql (closes: #32). - This code was contributed by user Antoine Beaupre ("The Anarcat"). - I tweaked it slightly, added configuration tests, and updated the manual. - I have no PostgreSQL databases on which to test the functionality. * Made most unit tests run properly on Windows platform, just for fun. * Re-implement Pipe class (under executeCommand) for Python 2.4+ - After Python 2.4, cross-platform subprocess.Popen class is available - Added some new regression tests for executeCommand to stress new Pipe * Switch to newer version of Docbook XSL stylesheet (1.68.1) - The old stylesheet isn't easily available any more (gone from sf.net) - Unfortunately, the PDF output changed somewhat with the new version * Add support for collecting individual files (closes: #30). - Create new config.CollectFile class for use by other classes - Update config.CollectConfig class to contain a list of collect files - Update config.Config class to parse and emit collect file data - Modified collect process in action.py to handle collect files - Updated user manual to discuss new configuraton Version 2.7.2 22 Dec 2005 * Remove some bogus writer tests that depended on an arbitrary SCSI device. Version 2.7.1 13 Dec 2005 * Tweak the CREDITS file to fix a few typos. * Remove completed tasks in TODO file and reorganize it slightly. * Get rid of sys.exit() calls in util/test.py in favor of simple returns. * Fix implementation of BackupFileList.removeUnchanged(captureDigest=True). - Since version 2.7.0, digest only included backed-up (unchanged) files - This release fixes code so digest is captured for all files in the list - Fixed captureDigest test cases, which were testing for wrong results * Make some more updates to the user manual based on further proof-reading. - Rework description of "midnight boundary" warning slightly in basic.xml - Change "Which Linux Distribution?" to "Which Platform?" in config.xml - Fix a few typos and misspellings in basic.xml Version 2.7.0 30 Oct 2005 * Cleanup some maintainer-only (non-distributed) Makefile rules. * Make changes to standardize file headers with other Cedar Solutions code. * Add debug statements to filesystem code (huge increase in debug log size). * Standardize some config variable names ("parentNode" instead of "parent"). * Fix util/test.py to return proper (non-zero) return status upon failure. * No longer attempt to change ownership of files when not running as root. * Remove regression test for bug #25 (testAddFile_036) 'cause it's not portable. * Modify use of user/password in MySQL extension (suggested by Matthias Urlichs). - Make user and password values optional in Cedar Backup configuration - Add a few regression tests to make sure configuration changes work - Add warning when user or password value(s) are visible in process listing - Document use of /root/.my.cnf or ~/.my.cnf in source code and user manual - Rework discussion of command line, file permissions, etc. in user manual * Optimize incremental backup, and hopefully speed it up a bit (closes: #29). - Change BackupFileList.removeUnchanged() to accept a captureDigest flag - This avoids need to call both generateDigestMap() and removeUnchanged() - Note that interface to removeUnchanged was modified, but not broken * Add support for pre- and post-action command hooks (closes: #27). - Added and sections within - Updated user manual documentation for options configuration section - Create new config.PreActionHook and PostActionHook classes to hold hooks - Added new hooks list field on config.OptionsConfig class - Update ActionSet and ActionItem in cli to handle and execute hooks * Rework and abstract XML functionality, plus remove dependency on PyXML. - Refactor general XML utility code out of config.py into xmlutil.py - Create new isElement() function to eliminate need for Node references - Create new createInputDom(), createOutputDom() and serializeDom() functions - Use minidom XML parser rather than PyExpat.reader (much faster) - Hack together xmlutil.Serializer based on xml.dom.ext.PrettyPrint - Remove references to PyXML in manual's depends.xml and install.xml files - Add notes about PyXML code sourced from Fourthought, Inc. in CREDITS - Rework mysql and subversion unit tests in terms of new functions Version 2.6.1 27 Sep 2005 * Fix broken call to node.hasChildNodes (no parens) in config.py. * Make "pre-existing collect indicator" error more obvious (closes: #26). * Avoid failures for UTF-8 filenames on certain filesystems (closes: #25). * Fix FilesystemList to encode excludeList items, preventing UTF-8 failures. Version 2.6.0 12 Sep 2005 * Remove bogus check for remote collect directory on master (closes: #18). * Fix testEncodePath_009 test failure on UTF-8 filesystems (closes: #19). * Fixed several unit tests related to the CollectConfig class (all typos). * Fix filesystem and action code to properly handle path "/" (closes: #24). * Add extension configuration to cback.conf.sample, to clarify things. * Place starting and ending revision numbers into Subversion dump filenames. * Implement resolver mechanism to support paths to commands (closes: #22). - Added section within configuration - Create new config.CommandOverride class to hold overrides - Added new overrides field on config.OptionsConfig class - Create util.PathResolverSingleton class to encapsulate mappings - Create util.resolveCommand convenience function for code to call - Create and call new _setupPathResolver() function in cli code - Change all _CMD constants to _COMMAND, for consistency * Change Subversion extension to support "fsfs" repositories (closes: #20). - Accept "FSFS" repository in configuration section - Create new FSFSRepository class to represent an FSFS repository - Refactor internal code common to both BDB and FSFS repositories - Add and rework test cases to provide coverage of FSFSRepository * Port to Darwin (Mac OS X) and ensure that all regression tests pass. - Don't run testAddDirContents_072() for Darwin (tarball's invalid there) - Write new ISO mount testing methods in terms of Apple's "hdiutil" utility - Accept Darwin-style SCSI writer devices, i.e. "IOCompactDiscServices" - Tweak existing SCSI id pattern to allow spaces in a few other places - Add new regression tests for validateScsiId() utility function - Add code warnings and documentation in manual and in doc/osx * Update, clean up and extend Cedar Backup User Manual (closes: #21). - Work through document and copy-edit it now that it's matured - Add documentation for new options and subversion config items - Exorcise references to Linux which assumed it was "the" platform - Add platform-specific notes for non-Linux platforms (darwin, BSDs) - Clarify purpose of the 'collect' action on the master - Clarify how actions (i.e. 'store') are optional - Clarify that 'all' does not execute extensions - Add an appendix on restoring backups Version 2.5.0 12 Jul 2005 * Update docs to modify use of "secure" (suggested by Lars Wirzenius). * Removed "Not an Official Debian Package" section in software manual. * Reworked Debian install procedure in manual to reference official packages. * Fix manual's build process to create files with mode 664 rather than 755. * Deal better with date boundaries on the store operation (closes: #17). - Add value in configuration - Add warnMidnite field to the StoreConfig object - Add warning in store process for crossing midnite boundary - Change store --full to have more consistent behavior - Update manual to document changes related to this bug Version 2.4.2 23 Apr 2005 * Fix boundaries log message again, properly this time. * Fix a few other log messages that used "," rather than "%". Version 2.4.1 22 Apr 2005 * Fix minor typos in user manual and source code documentation. * Properly annotate code implemented based on Python 2.3 source. * Add info within CREDITS about Python 2.3 and Docbook XSL licenses. * Fix logging for boundaries values (can't print None[0], duh). Version 2.4.0 02 Apr 2005 * Re-license manual under "GPL with clarifications" to satisfy DFSG. * Rework our unmount solution again to try and fix observed problems. - Sometimes, unmount seems to "work" but leaves things mounted. - This might be because some file is not yet completely closed. - We try to work around this by making repeated unmount attempts. - This logic is now encapsulated in util.mount() and util.unmount(). - This solution should also be more portable to non-Linux systems. Version 2.3.1 23 Mar 2005 * Attempt to deal more gracefully with corrupted media. * Unmount media using -l ("lazy unmount") in consistency check. * Be more verbose about media errors during consistency check. Version 2.3.0 10 Mar 2005 * Make 'extend' package public by listing it in CedarBackup2/__init__.py. * Reimplement digest generation to use incremental method (now ~3x faster). * Tweak manifest to be a little more selective about what's distributed. Version 2.2.0 09 Mar 2005 * Fix bug related to execution of commands with huge output. * Create custom class util.Pipe, inheriting from popen2.Popen4. * Re-implement util.executeCommand() in terms of util.Pipe. * Change ownership of sysinfo files to backup user/group after write. Version 2.1.3 08 Mar 2005 * In sysinfo extension, explicitly path to /sbin/fdisk command. * Modify behavior and logging when optional sysinfo commands are not found. * Add extra logging around boundaries and capacity calculations in writer.py. * In executeCommand, log command using output logger as well as debug level. * Docs now suggest --output in cron command line to aid problem diagnosis. * Fix bug in capacity calculation, this time for media with a single session. * Validate all capacity code against v1.0 code, making changes as needed. * Re-evaluate all capacity-related regression tests against v1.0 code. * Add new regression tests for capacity bugs which weren't already detected. Version 2.1.2 07 Mar 2005 * Fix a few extension error messages with incorrect (missing) arguments. * In sysinfo extension, do not log ls and dpkg output to the debug log. * Fix CdWriter, which reported negative capacity when disc was almost full. * Make displayBytes deal properly with negative values via math.fabs(). * Change displayBytes to default to 2 digits after the decimal point. Version 2.1.1 06 Mar 2005 * Fix bug in setup.py (need to install extensions properly). Version 2.1.0 06 Mar 2005 * Fixed doc/cback.1 .TH line to give proper manpage section. * Updated README to more completely describe what Cedar Backup is. * Fix a few logging statements for the collect action, to be clearer. * Fix regression tests that failed in a Debian pbuilder environment. * Add simple main routine to cli.py, so executing it is the same as cback. * Added optional outputFile and doNotLog parameters to util.executeCommand(). * Display byte quantities in sensible units (i.e. bytes, kB, MB) when logged. * Refactored private code into public in action.py and config.py. * Created MySQL extension in CedarBackup2.extend.mysql. * Created sysinfo extension in CedarBackup2.extend.sysinfo. * Created Subversion extension in CedarBackup2.extend.subversion. * Added regression tests as needed for new extension functionality. * Added Chapter 5, Official Extensions in the user manual. Version 2.0.0 26 Feb 2005 * Complete ground-up rewrite for 2.0.0 release. * See doc/release.txt for more details about changes. Version 1.13 25 Jan 2005 * Fix boundaries calculation when using kernel >= 2.6.8 (closes: #16). * Look for a matching boundaries pattern among all lines, not just the first. Version 1.12 16 Jan 2005 * Add support for ATAPI devices, just like ATA (closes: #15). * SCSI id can now be in the form '[ATA:|ATAPI:]scsibus,target,lun'. Version 1.11 17 Oct 2004 * Add experimental support for new Linux 2.6 ATA CD devices. * SCSI id can now be in the form '[ATA:]scsibus,target,lun'. * Internally, the SCSI id is now stored as a string, not a list. * Cleaned up 'cdrecord' calls in cdr.py to make them consistent. * Fixed a pile of warnings noticed by the latest pychecker. Version 1.10 01 Dec 2003 * Removed extraneous error parameter from cback's version() function. * Changed copyright statement and year; added COPYRIGHT in release.py. * Reworked all file headers to match new Cedar Solutions standard. * Removed __version__ and __date__ values with switch to Subversion. * Convert to tabs in Changelog to make the Vim syntax file happy. * Be more stringent in validating contents of SCSI triplet values. * Fixed bug when using modulo 1 (% 1) in a few places. * Fixed shell-interpolation bug discovered by Rick Low (security hole). * Replace all os.popen() calls with new execute_command() call for safety. Version 1.9 09 Nov 2002 * Packaging changes to allow Debian version to be "normal", not Debian-native. * Added CedarBackup/release.py to contain "upstream" release number. * Added -V,--version option to cback script. * Rewrote parts of Makefile to remove most Debian-specific rules. * Changed Makefile and setup.py to get version info from release.py. * The setup.py script now references /usr/bin/env python, not python2.2. * Debian-related changes will now reside exclusively in debian/changelog. Version 1.8 14 Oct 2002 * Fix bug with the way the default mode is displayed in the help screen. Version 1.7 14 Oct 2002 * Bug fix. Upgrade to Python 2.2.2b1 exposed a flaw in my version-check code. Version 1.6 06 Oct 2002 * Debian packaging cleanup (should have been a Debian-only release 1.5-2). Version 1.5 19 Sep 2002 * Changed cback script to more closely control ownership of logfile. Version 1.4 10 Sep 2002 * Various packaging cleanups. * Fixed code that reported negative capacity on a full disc. * Now blank disc ahead of time if it needs to be blanked. * Moved to Python2.2 for cleaner packaging (True, False, etc.) Version 1.3 20 Aug 2002 * Initial "public" release. ----------------------------------------------------------------------------- vim: set ft=changelog noexpandtab: ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.136956 cedar_backup3-3.8.1/LICENSE0000644000000000000000000004310514567004737012164 0ustar00 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 How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 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. , 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. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.136956 cedar_backup3-3.8.1/NOTICE0000644000000000000000000002154014567004737012062 0ustar00# vim: set ft=text80: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Project : Cedar Backup, release 3 # Purpose : Credits for package # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ########## # Credits ########## Unless otherwise indicated, all material in this repository is under the following license: Copyright (c) 2004-2024 Kenneth J. Pronovici This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. 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. The contents of the GNU General Public License can be found in the LICENSE file, or can be downloaded from http://www.gnu.org/ . Most of the source code in this project was written by Kenneth J. Pronovici. Some portions have been based on other pieces of open-source software, as indicated in the source code itself. Various patches have been contributed to the Cedar Backup codebase by Dmitry Rutsky. Major contributions include the initial implementation for the optimized media blanking strategy as well as improvements to the DVD writer implementation. The PostgreSQL extension was contributed by Antoine Beaupre ("The Anarcat"), based on the existing MySQL extension. Lukasz K. Nowak helped debug the split functionality and also provided patches for parts of the documentation. Zoran Bosnjak contributed changes to collect.py to implement recursive collect behavior based on recursion level. Jan Medlock contributed patches to improve the manpage and to support recent versions of the /usr/bin/split command. Minor code snippets derived from newsgroup and mailing list postings are not generally attributed unless I used someone else's source code verbatim. Source code annotated as "(c) 2001, 2002 Python Software Foundation" was originally taken from or derived from code within the Python 2.3 codebase. This code was released under the Python 2.3 license, which is an MIT-style academic license. Items under this license include the function util.getFunctionReference(). Source code annotated as "(c) 2000-2004 CollabNet" was originally released under the CollabNet License, which is an Apache/BSD-style license. Items under this license include basic markup and stylesheets used in creating the user manual. Source code annotated as "(c) 2000 Fourthought Inc, USA" was taken from or derived from code within the PyXML distribution and was originally part of the 4DOM suite developed by Fourthought, Inc. Fourthought released the code under a BSD-like license. Items under this license include the XML pretty-printing functionality implemented in xmlutil.py. #################### # CollabNet License #################### /* ================================================================ * Copyright (c) 2000-2004 CollabNet. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowledgment: "This product includes * software developed by CollabNet (http://www.Collab.Net/)." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The hosted project names must not be used to endorse or promote * products derived from this software without prior written * permission. For written permission, please contact info@collab.net. * * 5. Products derived from this software may not use the "Tigris" name * nor may "Tigris" appear in their names without prior written * permission of CollabNet. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of CollabNet. */ ##################### # Python 2.3 License ##################### PSF LICENSE AGREEMENT FOR PYTHON 2.3 ------------------------------------ 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"), and the Individual or Organization ("Licensee") accessing and otherwise using Python 2.3 software in source or binary form and its associated documentation. 2. Subject to the terms and conditions of this License Agreement, PSF hereby grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python 2.3 alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002 Python Software Foundation; All Rights Reserved" are retained in Python 2.3 alone or in any derivative version prepared by Licensee. 3. In the event Licensee prepares a derivative work that is based on or incorporates Python 2.3 or any part thereof, and wants to make the derivative work available to others as provided herein, then Licensee hereby agrees to include in any such work a brief summary of the changes made to Python 2.3. 4. PSF is making Python 2.3 available to Licensee on an "AS IS" basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.3 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.3, OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. 6. This License Agreement will automatically terminate upon a material breach of its terms and conditions. 7. Nothing in this License Agreement shall be deemed to create any relationship of agency, partnership, or joint venture between PSF and Licensee. This License Agreement does not grant permission to use PSF trademarks or trade name in a trademark sense to endorse or promote products or services of Licensee, or any third party. 8. By copying, installing or otherwise using Python 2.3, Licensee agrees to be bound by the terms and conditions of this License Agreement. ###################### # Fourthought License ###################### * Copyright (c) 2000 Fourthought Inc, USA * All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of FourThought LLC not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. FOURTHOUGHT LLC DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL FOURTHOUGHT BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.136956 cedar_backup3-3.8.1/PyPI.md0000644000000000000000000000541514567004737012324 0ustar00[![pypi](https://img.shields.io/pypi/v/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![license](https://img.shields.io/pypi/l/cedar-backup3.svg)](https://github.com/pronovic/cedar-backup3/blob/master/LICENSE) [![wheel](https://img.shields.io/pypi/wheel/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![python](https://img.shields.io/pypi/pyversions/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![Test Suite](https://github.com/pronovic/cedar-backup3/workflows/Test%20Suite/badge.svg)](https://github.com/pronovic/cedar-backup3/actions?query=workflow%3A%22Test+Suite%22) [![docs](https://readthedocs.org/projects/cedar-backup3/badge/?version=stable&style=flat)](https://cedar-backup3.readthedocs.io/en/stable/) [![coverage](https://coveralls.io/repos/github/pronovic/cedar-backup3/badge.svg?branch=master)](https://coveralls.io/github/pronovic/cedar-backup3?branch=master) [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/) [Cedar Backup](https://github.com/pronovic/cedar-backup3) is a software package designed to manage system backups for a pool of local and remote machines. Cedar Backup understands how to back up filesystem data as well as MySQL and PostgreSQL databases and Subversion repositories. It can also be easily extended to support other kinds of data sources. Cedar Backup is focused around weekly backups to a single CD or DVD disc, with the expectation that the disc will be changed or overwritten at the beginning of each week. If your hardware is new enough, Cedar Backup can write multisession discs, allowing you to add incremental data to a disc on a daily basis. Alternately, Cedar Backup can write your backups to the Amazon S3 cloud rather than relying on physical media. See the [Cedar Backup v3 Software Manual](https://cedar-backup3.readthedocs.io/en/stable/manual/index.html) for details. Besides offering command-line utilities to manage the backup process, Cedar Backup provides a well-organized library of backup-related functionality. For more information, see the [API Reference](https://cedar-backup3.readthedocs.io/en/stable/autoapi/index.html). There are many different backup software systems in the open source world. Cedar Backup aims to fill a niche: it aims to be a good fit for people who need to back up a limited amount of important data on a regular basis. Cedar Backup isn’t for you if you want to back up your huge MP3 collection every night, or if you want to back up a few hundred machines. However, if you administer a small set of machines and you want to run daily incremental backups for things like system configuration, current email, small web sites, source code repositories, or small databases, then Cedar Backup is probably worth your time. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.136956 cedar_backup3-3.8.1/README.md0000644000000000000000000001314114567004737012433 0ustar00# Cedar Backup v3 [![pypi](https://img.shields.io/pypi/v/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![license](https://img.shields.io/pypi/l/cedar-backup3.svg)](https://github.com/pronovic/cedar-backup3/blob/master/LICENSE) [![wheel](https://img.shields.io/pypi/wheel/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![python](https://img.shields.io/pypi/pyversions/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![Test Suite](https://github.com/pronovic/cedar-backup3/workflows/Test%20Suite/badge.svg)](https://github.com/pronovic/cedar-backup3/actions?query=workflow%3A%22Test+Suite%22) [![docs](https://readthedocs.org/projects/cedar-backup3/badge/?version=stable&style=flat)](https://cedar-backup3.readthedocs.io/en/stable/) [![coverage](https://coveralls.io/repos/github/pronovic/cedar-backup3/badge.svg?branch=master)](https://coveralls.io/github/pronovic/cedar-backup3?branch=master) [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/) ## What is Cedar Backup? [Cedar Backup v3](https://pypi.org/project/cedar-backup3/) is a software package designed to manage system backups for a pool of local and remote machines. The project was originally maintained at SourceForge, and historical releases still exist there. The project was moved to BitBucket in mid-2015, and from there to GitHub in mid-2019 when BitBucket retired their Mercurial hosting service. Cedar Backup understands how to back up filesystem data as well as MySQL and PostgreSQL databases and Subversion repositories. It can also be easily extended to support other kinds of data sources. The backup process is focused around weekly backups to a single CD or DVD disc, with the expectation that the disc will be changed or overwritten at the beginning of each week. Alternately, Cedar Backup can write your backups to the Amazon S3 cloud rather than relying on physical media. Besides offering command-line utilities to manage the backup process, Cedar Backup provides a well-organized library of backup-related functionality, written in the Python 3 programming language. There are many different backup software systems in the open source world. Cedar Backup aims to fill a niche: it aims to be a good fit for people who need to back up a limited amount of important data on a regular basis. Cedar Backup isn’t for you if you want to back up your huge MP3 collection every night, or if you want to back up a few hundred machines. However, if you administer a small set of machines and you want to run daily incremental backups for things like system configuration, current email, small web sites, source code repositories, or small databases, then Cedar Backup is probably worth your time. Cedar Backup has been developed on a [Debian GNU/Linux](http://www.debian.org/) system and is primarily supported on Debian and other Linux systems. However, since it is written in portable [Python 3](http://www.python.org), it should run without problems on just about any UNIX-like operating system. In particular, full Cedar Backup functionality is known to work on Debian and SuSE Linux, and client functionality is also known to work on FreeBSD and OS X systems. ## Supported Python Versions My general policy is to support the latest four versions of the Python interpreter. For example, starting in late 2023, I dropped support for Python 3.8, leaving support for Python 3.9, 3.10, 3.11, and 3.12. At my discretion, I may choose to support older versions, as long as doing so doesn't cause too much pain. These days Python is released about once per year, so most anyone should be able to run one of the supported versions, even on a long-term support Linux distribution. ## Developer Documentation Developer documentation is found in [DEVELOPER.md](DEVELOPER.md). See that file for notes about how the code is structured, how to set up a development environment, etc. ## End User Documentation See the [Changelog](https://github.com/pronovic/cedar-backup3/blob/master/Changelog) for recent changes. The [Cedar Backup v3 Software Manual](https://cedar-backup3.readthedocs.io/en/stable/manual/index.html) documents the process of setting up and using Cedar Backup. In the manual, you can find information about how Cedar Backup works, how to install and configure it, how to schedule backups, how to restore data, and how to get support. ## Library Code The Cedar Backup v3 has been designed as both an application and a library of backup-related functionality. The `CedarBackup3` Python package contains a variety of useful backup-related classes and functions. For instance: the `IsoImage` class represents an ISO CD image; the `CdWriter` class represents a CD-R/CD-RW writer device; and the `FilesystemList` class represents a list of files and directories on a filesystem. For more information, see the [API Reference](https://cedar-backup3.readthedocs.io/en/stable/autoapi/index.html) documentation. ## Package Distributions Cedar Backup is primarily distributed as a Python 3 package. You can install it from [PyPI](https://pypi.org/project/cedar-backup3/) using pip: ``` $ pip install cedar-backup3 ``` In addition to the Python package, Cedar Backup requires a variety of external system dependencies. For more information, see the [Cedar Backup v3 Software Manual](https://cedar-backup3.readthedocs.io/en/stable/manual/install.html#installing-the-python-package). Debian packages for Cedar Backup v3, called `cedar-backup3` and `cedar-backup3-doc`, were first available starting with the Debian 'stretch' release. Debian derivatives (such as Ubuntu) should also contain the packages. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.136956 cedar_backup3-3.8.1/docs/.gitignore0000644000000000000000000000002614567004737014072 0ustar00*.pyc *.pyo .DS_Store ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/.sphinxignore0000644000000000000000000000021614567004737014621 0ustar00WARNING: document isn't included in any toctree WARNING: duplicate object description WARNING: more than one target found for cross-reference ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/Makefile0000644000000000000000000001523614567004737013553 0ustar00# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ReadtheDocsTemplate.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ReadtheDocsTemplate.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/ReadtheDocsTemplate" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ReadtheDocsTemplate" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/README.md0000644000000000000000000000017014567004737013361 0ustar00Developer and user documentation. Design and themes gratefully copied from [Requests](https://github.com/psf/requests). ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/_static/custom.css0000644000000000000000000000020214567004737015550 0ustar00body > div.document > div.sphinxsidebar > div > form > table > tbody > tr:nth-child(2) > td > select { width: 100%!important; } ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/_themes/LICENSE0000644000000000000000000000350514567004737014540 0ustar00Modifications: Copyright (c) 2011 Kenneth Reitz. Original Project: Copyright (c) 2010 by Armin Ronacher. Some rights reserved. Redistribution and use in source and binary forms of the theme, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. We kindly ask you to only use these themes in an unmodified manner just for Flask and Flask-related products, not for unrelated projects. If you like the visual style and want to use it for your own projects, please consider making some larger changes to the themes (such as changing font faces, sizes, colors or margins). THIS THEME IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS THEME, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/_themes/flask_theme_support.py0000644000000000000000000000750114567004737020163 0ustar00# flasky extensions. flasky pygments style based on tango style from pygments.style import Style from pygments.token import ( Keyword, Name, Comment, String, Error, Number, Operator, Generic, Whitespace, Punctuation, Other, Literal, ) class FlaskyStyle(Style): background_color = "#f8f8f8" default_style = "" styles = { # No corresponding class for the following: # Text: "", # class: '' Whitespace: "underline #f8f8f8", # class: 'w' Error: "#a40000 border:#ef2929", # class: 'err' Other: "#000000", # class 'x' Comment: "italic #8f5902", # class: 'c' Comment.Preproc: "noitalic", # class: 'cp' Keyword: "bold #004461", # class: 'k' Keyword.Constant: "bold #004461", # class: 'kc' Keyword.Declaration: "bold #004461", # class: 'kd' Keyword.Namespace: "bold #004461", # class: 'kn' Keyword.Pseudo: "bold #004461", # class: 'kp' Keyword.Reserved: "bold #004461", # class: 'kr' Keyword.Type: "bold #004461", # class: 'kt' Operator: "#582800", # class: 'o' Operator.Word: "bold #004461", # class: 'ow' - like keywords Punctuation: "bold #000000", # class: 'p' # because special names such as Name.Class, Name.Function, etc. # are not recognized as such later in the parsing, we choose them # to look the same as ordinary variables. Name: "#000000", # class: 'n' Name.Attribute: "#c4a000", # class: 'na' - to be revised Name.Builtin: "#004461", # class: 'nb' Name.Builtin.Pseudo: "#3465a4", # class: 'bp' Name.Class: "#000000", # class: 'nc' - to be revised Name.Constant: "#000000", # class: 'no' - to be revised Name.Decorator: "#888", # class: 'nd' - to be revised Name.Entity: "#ce5c00", # class: 'ni' Name.Exception: "bold #cc0000", # class: 'ne' Name.Function: "#000000", # class: 'nf' Name.Property: "#000000", # class: 'py' Name.Label: "#f57900", # class: 'nl' Name.Namespace: "#000000", # class: 'nn' - to be revised Name.Other: "#000000", # class: 'nx' Name.Tag: "bold #004461", # class: 'nt' - like a keyword Name.Variable: "#000000", # class: 'nv' - to be revised Name.Variable.Class: "#000000", # class: 'vc' - to be revised Name.Variable.Global: "#000000", # class: 'vg' - to be revised Name.Variable.Instance: "#000000", # class: 'vi' - to be revised Number: "#990000", # class: 'm' Literal: "#000000", # class: 'l' Literal.Date: "#000000", # class: 'ld' String: "#4e9a06", # class: 's' String.Backtick: "#4e9a06", # class: 'sb' String.Char: "#4e9a06", # class: 'sc' String.Doc: "italic #8f5902", # class: 'sd' - like a comment String.Double: "#4e9a06", # class: 's2' String.Escape: "#4e9a06", # class: 'se' String.Heredoc: "#4e9a06", # class: 'sh' String.Interpol: "#4e9a06", # class: 'si' String.Other: "#4e9a06", # class: 'sx' String.Regex: "#4e9a06", # class: 'sr' String.Single: "#4e9a06", # class: 's1' String.Symbol: "#4e9a06", # class: 'ss' Generic: "#000000", # class: 'g' Generic.Deleted: "#a40000", # class: 'gd' Generic.Emph: "italic #000000", # class: 'ge' Generic.Error: "#ef2929", # class: 'gr' Generic.Heading: "bold #000080", # class: 'gh' Generic.Inserted: "#00A000", # class: 'gi' Generic.Output: "#888", # class: 'go' Generic.Prompt: "#745334", # class: 'gp' Generic.Strong: "bold #000000", # class: 'gs' Generic.Subheading: "bold #800080", # class: 'gu' Generic.Traceback: "bold #a40000", # class: 'gt' } ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/conf.py0000644000000000000000000003117714567004737013414 0ustar00# -*- coding: utf-8 -*- # pylint: skip-file # # Cedar Backup v3 documentation build configuration file, based on existing # documentation for Requests (https://github.com/psf/requests). # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys from pathlib import Path from importlib_metadata import metadata # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # Insert the source tree into the system path sys.path.insert(0, os.path.abspath("../src")) sys.path.insert(0, os.path.abspath("_themes")) # Configure the GitHub repository GITHUB_OWNER = "pronovic" GITHUB_REPO = "cedar-backup3" # Configure attributes based on metadata _METADATA = metadata("cedar-backup3") PROJECT = _METADATA["Name"] SUMMARY = _METADATA["Summary"] AUTHOR = _METADATA["Author"] VERSION = _METADATA["Version"] # Dump metadata so it's obvious in the build log print("GitHub repo: %s/%s" % (GITHUB_OWNER, GITHUB_REPO)) print("Project....: %s" % PROJECT) print("Summary....: %s" % SUMMARY) print("Author.....: %s" % AUTHOR) print("Version....: %s" % VERSION) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ "sphinx.ext.todo", "sphinx.ext.napoleon", "autoapi.extension", ] # Napoleon settings for docstrings napoleon_google_docstring = True napoleon_numpy_docstring = False napoleon_include_init_with_doc = True napoleon_include_private_with_doc = False napoleon_include_special_with_doc = False napoleon_use_admonition_for_examples = False napoleon_use_admonition_for_notes = False napoleon_use_admonition_for_references = False napoleon_use_ivar = False napoleon_use_param = True napoleon_use_rtype = True autodoc_member_order = "bysource" # Auto-api settings autoapi_type = "python" autoapi_dirs = ["../src"] autoapi_add_toctree_entry = True autoapi_options = ['members', 'undoc-members', 'show-inheritance', 'special-members'] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = "index" # General information about the project. project = PROJECT copyright = "(c) %s" % AUTHOR author = AUTHOR # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = VERSION # The full version, including alpha/beta/rc tags. release = VERSION # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = "en" # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = False # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "flask_theme_support.FlaskyStyle" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # todo_include_todos = True # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = "alabaster" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. html_theme_options = { "show_powered_by": False, "github_user": GITHUB_OWNER, "github_repo": GITHUB_REPO, "github_banner": True, "show_related": False, "note_bg": "#FFF59C", } # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. html_use_smartypants = False # Custom sidebar templates, maps document names to template names. # html_sidebars = { "index": [ "hacks.html"], } # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = PROJECT # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [(master_doc, "%s.tex" % PROJECT, "%s Documentation" % PROJECT, AUTHOR, "manual")] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [(master_doc, PROJECT, "%s Documentation" % PROJECT, [author], 1)] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ( master_doc, PROJECT, "%s Documentation" % PROJECT, author, PROJECT, SUMMARY, "Miscellaneous", ) ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Epub output ---------------------------------------------- # Bibliographic Dublin Core info. epub_title = project epub_author = author epub_publisher = author epub_copyright = copyright # The basename for the epub file. It defaults to the project name. # epub_basename = project # The HTML theme for the epub output. Since the default themes are not # optimized for small screen space, using the same theme for HTML and epub # output is usually not wise. This defaults to 'epub', a theme designed to save # visual space. # epub_theme = 'epub' # The language of the text. It defaults to the language option # or 'en' if the language is not set. # epub_language = '' # The scheme of the identifier. Typical schemes are ISBN or URL. # epub_scheme = '' # The unique identifier of the text. This can be a ISBN number # or the project homepage. # epub_identifier = '' # A unique identification for the text. # epub_uid = '' # A tuple containing the cover image and cover page html template filenames. # epub_cover = () # A sequence of (type, uri, title) tuples for the guide element of content.opf. # epub_guide = () # HTML files that should be inserted before the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_pre_files = [] # HTML files that should be inserted after the pages created by sphinx. # The format is a list of tuples containing the path and title. # epub_post_files = [] # A list of files that should not be packed into the epub file. epub_exclude_files = ["search.html"] # The depth of the table of contents in toc.ncx. # epub_tocdepth = 3 # Allow duplicate toc entries. # epub_tocdup = True # Choose between 'default' and 'includehidden'. # epub_tocscope = 'default' # Fix unsupported image types using the Pillow. # epub_fix_images = False # Scale large images. # epub_max_image_width = 0 # How to display URL addresses: 'footnote', 'no', or 'inline'. # epub_show_urls = 'inline' # If false, no index is generated. # epub_use_index = True ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/index.rst0000644000000000000000000001324714567004737013754 0ustar00Cedar Backup v3 =============== Release v\ |version| .. image:: https://img.shields.io/pypi/v/cedar-backup3.svg :target: https://pypi.org/project/cedar-backup3/ .. image:: https://img.shields.io/pypi/l/cedar-backup3.svg :target: https://github.com/pronovic/cedar-backup3/blob/master/LICENSE .. image:: https://img.shields.io/pypi/wheel/cedar-backup3.svg :target: https://pypi.org/project/cedar-backup3/ .. image:: https://img.shields.io/pypi/pyversions/cedar-backup3.svg :target: https://pypi.org/project/cedar-backup3/ .. image:: https://github.com/pronovic/cedar-backup3/workflows/Test%20Suite/badge.svg :target: https://github.com/pronovic/cedar-backup3/actions?query=workflow%3A%22Test+Suite%22 .. image:: https://readthedocs.org/projects/cedar-backup3/badge/?version=stable&style=flat :target: https://cedar-backup3.readthedocs.io/en/stable/ .. image:: https://coveralls.io/repos/github/pronovic/cedar-backup3/badge.svg?branch=master :target: https://coveralls.io/github/pronovic/cedar-backup3?branch=master Cedar Backup is a software package designed to manage system backups for a pool of local and remote machines. Cedar Backup understands how to back up filesystem data as well as MySQL and PostgreSQL databases and Subversion repositories. It can also be easily extended to support other kinds of data sources. The backup process is focused around weekly backups to a single CD or DVD disc, with the expectation that the disc will be changed or overwritten at the beginning of each week. Alternately, Cedar Backup can write your backups to the Amazon S3 cloud rather than relying on physical media. Besides offering command-line utilities to manage the backup process, Cedar Backup provides a well-organized library of backup-related functionality, written in the Python 3 programming language. There are many different backup software systems in the open source world. Cedar Backup aims to fill a niche: it aims to be a good fit for people who need to back up a limited amount of important data on a regular basis. Cedar Backup isn’t for you if you want to back up your huge MP3 collection every night, or if you want to back up a few hundred machines. However, if you administer a small set of machines and you want to run daily incremental backups for things like system configuration, current email, small web sites, source code repositories, or small databases, then Cedar Backup is probably worth your time. Documentation ------------- See the Changelog_ for recent changes. The :doc:`manual/index` documents the process of setting up and using Cedar Backup. In the manual, you can find information about how Cedar Backup works, how to install and configure it, how to schedule backups, how to restore data, and how to get support. Package Distributions --------------------- Cedar Backup is primarily distributed as a Python 3 package. You can install it using pip:: $ pip install cedar-backup3 In addition to the Python package, Cedar Backup requires a variety of external system dependencies. For more information, see the :doc:`manual/index`. Debian packages for Cedar Backup v3, called ``cedar-backup3`` and ``cedar-backup3-doc``, were first available starting with the Debian 'stretch' release. Debian derivatives (such as Ubuntu) should also contain the packages. Library Code ------------ The Cedar Backup v3 has been designed as both an application and a library of backup-related functionality. The ``CedarBackup3`` Python package contains a variety of useful backup-related classes and functions. For instance: the ``IsoImage`` class represents an ISO CD image; the ``CdWriter`` class represents a CD-R/CD-RW writer device; and the ``FilesystemList`` class represents a list of files and directories on a filesystem. .. toctree:: :maxdepth: 1 Contributing Improvements ------------------------- Users are welcome to contribute improvements to Cedar Backup. In the past, users have helped out by reporting unit test failures, making suggestions, requesting enhancements, updating documentation, submitting patches, and beta-testing entire releases or individual bug fixes. As a result, Cedar Backup has evolved into a much more flexible platform than it would otherwise have been. Migrating from Version 2 to Version 3 ------------------------------------- The main difference between Cedar Backup v2 and Cedar Backup v3 is the targeted Python interpreter. Cedar Backup v2 was designed for Python 2, while v3 is a conversion of the original code to Python 3. Other than that, both versions are functionally equivalent. The configuration format is unchanged, and you can mix-and-match masters and clients of different versions in the same backup pool. However, v2 is no longer maintained, so you should convert as soon as possible. A major design goal for v3 was to facilitate easy migration testing for users, by making it possible to install v3 on the same server where v2 was already in use. A side effect of this design choice is that all of the executables, configuration files, and logs changed names in v3. Where v2 used ``cback``, v3 uses ``cback3``: ``cback3.conf`` instead of ``cback.conf``, ``cback3.log`` instead of ``cback.log``, etc. So, while migrating from v2 to v3 is relatively straightforward, you will have to make some changes manually. You will need to create a new configuration file (or soft link to the old one), modify your cron jobs to use the new executable name, etc. You can migrate one server at a time in your pool with no ill effects, or even incrementally migrate a single server by using v2 and v3 on different days of the week or for different parts of the backup. .. _Changelog: https://github.com/pronovic/cedar-backup3/blob/master/Changelog ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/make.bat0000644000000000000000000001506514567004737013520 0ustar00@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\complexity.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\complexity.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/basic.rst0000644000000000000000000005734314567004737015210 0ustar00.. _cedar-basic: Basic Concepts ============== .. _cedar-basic-general: General Architecture -------------------- Cedar Backup is architected as a Python package (library) and a single executable (a Python script). The Python package provides both application-specific code and general utilities that can be used by programs other than Cedar Backup. It also includes modules that can be used by third parties to extend Cedar Backup or provide related functionality. The ``cback3`` script is designed to run as root, since otherwise it's difficult to back up system directories or write to the CD/DVD device. However, pains are taken to use the backup user's effective user id (specified in configuration) when appropriate. *Note:* this does not mean that ``cback3`` runs setuid [1]_ or setgid. However, all files on disk will be owned by the backup user, and and all rsh-based network connections will take place as the backup user. The ``cback3`` script is configured via command-line options and an XML configuration file on disk. The configuration file is normally stored in ``/etc/cback3.conf``, but this path can be overridden at runtime. See :doc:`config` for more information on how Cedar Backup is configured. |warning| You should be aware that backups to CD/DVD media can probably be read by any user which has permissions to mount the CD/DVD writer. If you intend to leave the backup disc in the drive at all times, you may want to consider this when setting up device permissions on your machine. See also :doc:`extensions`. .. _cedar-basic-datarecovery: Data Recovery ------------- Cedar Backup does not include any facility to restore backups. Instead, it assumes that the administrator (using the procedures and references in :doc:`recovering`) can handle the task of restoring their own system, using the standard system tools at hand. If I were to maintain recovery code in Cedar Backup, I would almost certainly end up in one of two situations. Either Cedar Backup would only support simple recovery tasks, and those via an interface a lot like that of the underlying system tools; or Cedar Backup would have to include a hugely complicated interface to support more specialized (and hence useful) recovery tasks like restoring individual files as of a certain point in time. In either case, I would end up trying to maintain critical functionality that would be rarely used, and hence would also be rarely tested by end-users. I am uncomfortable asking anyone to rely on functionality that falls into this category. My primary goal is to keep the Cedar Backup codebase as simple and focused as possible. I hope you can understand how the choice of providing documentation, but not code, seems to strike the best balance between managing code complexity and providing the functionality that end-users need. .. _cedar-basic-pools: Cedar Backup Pools ------------------- There are two kinds of machines in a Cedar Backup pool. One machine (the *master*) has a CD or DVD writer on it and writes the backup to disc. If you are using Amazon S3 instead of physical media, then the master is the machine that consolidates data and writes your backup to cloud storage. The other machines (*clients*) collect data to be written to disc by the master. Collectively, the master and client machines in a pool are called *peer machines*. Cedar Backup has been designed primarily for situations where there is a single master and a set of other clients that the master interacts with. However, it will just as easily work for a single machine (a backup pool of one) and in fact more users seem to use it like this than any other way. .. _cedar-basic-process: The Backup Process ------------------ The Cedar Backup backup process is structured in terms of a set of decoupled actions which execute independently (based on a schedule in ``cron``) rather than through some highly coordinated flow of control. This design decision has both positive and negative consequences. On the one hand, the code is much simpler and can choose to simply abort or log an error if its expectations are not met. On the other hand, the administrator must coordinate the various actions during initial set-up. See *Coordination between Master and Clients* (later in this chapter) for more information on this subject. A standard backup run consists of four steps (actions), some of which execute on the master machine, and some of which execute on one or more client machines. These actions are: collect, stage, store and purge. In general, more than one action may be specified on the command-line. If more than one action is specified, then actions will be taken in a sensible order (generally *collect*, *stage*, *store*, *purge*). A special *all* action is also allowed, which implies all of the standard actions in the same sensible order. The ``cback3`` command also supports several actions that are not part of the standard backup run and cannot be executed along with any other actions. These actions are *validate*, *initialize* and *rebuild*. All of the various actions are discussed further below. See :doc:`config` for more information on how a backup run is configured. Cedar Backup was designed to be flexible. It allows you to decide for yourself which backup steps you care about executing (and when you execute them), based on your own situation and your own priorities. For example, no need to write to disc or to Amazon S3 at all. In fact, some users prefer to use their master machine as a simple “consolidation point”. They don't back up any data on the master, and don't write to disc at all. They just use Cedar Backup to handle the mechanics of moving backed-up data to a central location. This isn't quite what Cedar Backup was written to do, but it is flexible enough to meet their needs. .. _cedar-basic-process-collect: The Collect Action ~~~~~~~~~~~~~~~~~~ The collect action is the first action in a standard backup run. It executes on both master and client nodes. Based on configuration, this action traverses the peer's filesystem and gathers files to be backed up. Each configured high-level directory is collected up into its own ``tar`` file in the collect directory. The tarfiles can either be uncompressed (``.tar``) or compressed with either ``gzip`` (``.tar.gz``) or ``bzip2`` (``.tar.bz2``). There are three supported collect modes: *daily*, *weekly* and *incremental*. Directories configured for daily backups are backed up every day. Directories configured for weekly backups are backed up on the first day of the week. Directories configured for incremental backups are traversed every day, but only the files which have changed (based on a saved-off cryptographic hash) are actually backed up. Collect configuration also allows for a variety of ways to filter files and directories out of the backup. For instance, administrators can configure an ignore indicator file or specify absolute paths or filename patterns to be excluded. You can even configure a backup “link farm” rather than explicitly listing files and directories in configuration. This action is optional on the master. You only need to configure and execute the collect action on the master if you have data to back up on that machine. If you plan to use the master only as a “consolidation point” to collect data from other machines, then there is no need to execute the collect action there. If you run the collect action on the master, it behaves the same there as anywhere else, and you have to stage the master's collected data just like any other client (typically by configuring a local peer in the stage action). .. _cedar-basic-process-stage: The Stage Action ~~~~~~~~~~~~~~~~ The stage action is the second action in a standard backup run. It executes on the master peer node. The master works down the list of peers in its backup pool and stages (copies) the collected backup files from each of them into a daily staging directory by peer name. For the purposes of this action, the master node can be configured to treat itself as a client node. If you intend to back up data on the master, configure the master as a local peer. Otherwise, just configure each of the clients as a remote peer. Local and remote client peers are treated differently. Local peer collect directories are assumed to be accessible via normal copy commands (i.e. on a mounted filesystem) while remote peer collect directories are accessed via an RSH-compatible command such as ``ssh``. If a given peer is not ready to be staged, the stage process will log an error, abort the backup for that peer, and then move on to its other peers. This way, one broken peer cannot break a backup for other peers which are up and running. Keep in mind that Cedar Backup is flexible about what actions must be executed as part of a backup. If you would prefer, you can stop the backup process at this step, and skip the store step. In this case, the staged directories will represent your backup rather than a disc or an Amazon S3 bucket. |note| Directories “collected” by another process can alsoalso be staged by Cedar Backup. If the file ``cback.collect`` exists in a collect directory when the stage action is taken, then that directory will be staged. .. _cedar-basic-process-store: The Store Action ~~~~~~~~~~~~~~~~ The store action is the third action in a standard backup run. It executes on the master peer node. The master machine determines the location of the current staging directory, and then writes the contents of that staging directory to disc. After the contents of the directory have been written to disc, an optional validation step ensures that the write was successful. If the backup is running on the first day of the week, if the drive does not support multisession discs, or if the ``--full`` option is passed to the ``cback3`` command, the disc will be rebuilt from scratch. Otherwise, a new ISO session will be added to the disc each day the backup runs. This action is entirely optional. If you would prefer to just stage backup data from a set of peers to a master machine, and have the staged directories represent your backup rather than a disc, this is fine. |warning| The store action is not supported on the Mac OS X (darwin) platform. On that platform, the “automount” function of the Finder interferes significantly with Cedar Backup's ability to mount and unmount media and write to the CD or DVD hardware. The Cedar Backup writer and image functionality works on this platform, but the effort required to fight the operating system about who owns the media and the device makes it nearly impossible to execute the store action successfully. The store action tries to be smart about finding the current staging directory. It first checks the current day's staging directory. If that directory exists, and it has not yet been written to disc (i.e. there is no store indicator), then it will be used. Otherwise, the store action will look for an unused staging directory for either the previous day or the next day, in that order. A warning will be written to the log under these circumstances (controlled by the ```` configuration value). This behavior varies slightly when the ``--full`` option is in effect. Under these circumstances, any existing store indicator will be ignored. Also, the store action will always attempt to use the current day's staging directory, ignoring any staging directories for the previous day or the next day. This way, running a full store action more than once concurrently will always produce the same results. (You might imagine a use case where a person wants to make several copies of the same full backup.) .. _cedar-basic-process-purge: The Purge Action ~~~~~~~~~~~~~~~~ The purge action is the fourth and final action in a standard backup run. It executes both on the master and client peer nodes. Configuration specifies how long to retain files in certain directories, and older files and empty directories are purged. Typically, collect directories are purged daily, and stage directories are purged weekly or slightly less often (if a disc gets corrupted, older backups may still be available on the master). Some users also choose to purge the configured working directory (which is used for temporary files) to eliminate any leftover files which might have resulted from changes to configuration. .. _cedar-basic-process-all: The All Action ~~~~~~~~~~~~~~ The all action is a pseudo-action which causes all of the actions in a standard backup run to be executed together in order. It cannot be combined with any other actions on the command line. Extensions *cannot* be executed as part of the all action. If you need to execute an extended action, you must specify the other actions you want to run individually on the command line. [2]_ The all action does not have its own configuration. Instead, it relies on the individual configuration sections for all of the other actions. .. _cedar-basic-process-validate: The Validate Action ~~~~~~~~~~~~~~~~~~~ The validate action is used to validate configuration on a particular peer node, either master or client. It cannot be combined with any other actions on the command line. The validate action checks that the configuration file can be found, that the configuration file is valid, and that certain portions of the configuration file make sense (for instance, making sure that specified users exist, directories are readable and writable as necessary, etc.). .. _cedar-basic-process-initialize: The Initialize Action ~~~~~~~~~~~~~~~~~~~~~ The initialize action is used to initialize media for use with Cedar Backup. This is an optional step. By default, Cedar Backup does not need to use initialized media and will write to whatever media exists in the writer device. However, if the “check media” store configuration option is set to true, Cedar Backup will check the media before writing to it and will error out if the media has not been initialized. Initializing the media consists of writing a mostly-empty image using a known media label (the media label will begin with “CEDAR BACKUP”). Note that only rewritable media (CD-RW, DVD+RW) can be initialized. It doesn't make any sense to initialize media that cannot be rewritten (CD-R, DVD+R), since Cedar Backup would then not be able to use that media for a backup. You can still configure Cedar Backup to check non-rewritable media; in this case, the check will also pass if the media is apparently unused (i.e. has no media label). .. _cedar-basic-process-rebuild: The Rebuild Action ~~~~~~~~~~~~~~~~~~ The rebuild action is an exception-handling action that is executed independent of a standard backup run. It cannot be combined with any other actions on the command line. The rebuild action attempts to rebuild “this week's” disc from any remaining unpurged staging directories. Typically, it is used to make a copy of a backup, replace lost or damaged media, or to switch to new media mid-week for some other reason. To decide what data to write to disc again, the rebuild action looks back and finds the first day of the current week. Then, it finds any remaining staging directories between that date and the current date. If any staging directories are found, they are all written to disc in one big ISO session. The rebuild action does not have its own configuration. It relies on configuration for other other actions, especially the store action. .. _cedar-basic-coordinate: Coordination between Master and Clients --------------------------------------- Unless you are using Cedar Backup to manage a “pool of one”, you will need to set up some coordination between your clients and master to make everything work properly. This coordination isn't difficult --- it mostly consists of making sure that operations happen in the right order --- but some users are suprised that it is required and want to know why Cedar Backup can't just “take care of it for me”. Essentially, each client must finish collecting all of its data before the master begins staging it, and the master must finish staging data from a client before that client purges its collected data. Administrators may need to experiment with the time between the collect and purge entries so that the master has enough time to stage data before it is purged. .. _cedar-basic-managedbackups: Managed Backups --------------- Cedar Backup also supports an optional feature called the “managed backup”. This feature is intended for use with remote clients where cron is not available. When managed backups are enabled, managed clients must still be configured as usual. However, rather than using a cron job on the client to execute the collect and purge actions, the master executes these actions on the client via a remote shell. To make this happen, first set up one or more managed clients in Cedar Backup configuration. Then, invoke Cedar Backup with the ``--managed`` command-line option. Whenever Cedar Backup invokes an action locally, it will invoke the same action on each of the managed clients. Technically, this feature works for any client, not just clients that don't have cron available. Used this way, it can simplify the setup process, because cron only has to be configured on the master. For some users, that may be motivation enough to use this feature all of the time. However, please keep in mind that this feature depends on a stable network. If your network connection drops, your backup will be interrupted and will not be complete. It is even possible that some of the Cedar Backup metadata (like incremental backup state) will be corrupted. The risk is not high, but it is something you need to be aware of if you choose to use this optional feature. .. _cedar-basic-mediadevice: Media and Device Types ---------------------- Cedar Backup is focused around writing backups to CD or DVD media using a standard SCSI or IDE writer. In Cedar Backup terms, the disc itself is referred to as the media, and the CD/DVD drive is referred to as the device or sometimes the backup device. When using a new enough backup device, a new “multisession” ISO image [3]_ is written to the media on the first day of the week, and then additional multisession images are added to the media each day that Cedar Backup runs. This way, the media is complete and usable at the end of every backup run, but a single disc can be used all week long. If your backup device does not support multisession images --- which is really unusual today --- then a new ISO image will be written to the media each time Cedar Backup runs (and you should probably confine yourself to the “daily” backup mode to avoid losing data). Cedar Backup currently supports four different kinds of CD media: cdr-74 74-minute non-rewritable CD media cdrw-74 74-minute rewritable CD media cdr-80 80-minute non-rewritable CD media cdrw-80 80-minute rewritable CD media I have chosen to support just these four types of CD media because they seem to be the most “standard” of the various types commonly sold in the U.S. as of this writing (early 2005). If you regularly use an unsupported media type and would like Cedar Backup to support it, send me information about the capacity of the media in megabytes (MB) and whether it is rewritable. Cedar Backup also supports two kinds of DVD media: dvd+r Single-layer non-rewritable DVD+R media dvd+rw Single-layer rewritable DVD+RW media The underlying ``growisofs`` utility does support other kinds of media (including DVD-R, DVD-RW and BlueRay) which work somewhat differently than standard DVD+R and DVD+RW media. I don't support these other kinds of media because I haven't had any opportunity to work with them. The same goes for dual-layer media of any type. .. _cedar-basic-incremental: Incremental Backups ------------------- Cedar Backup supports three different kinds of backups for individual collect directories. These are daily, weekly and incremental backups. Directories using the daily mode are backed up every day. Directories using the weekly mode are only backed up on the first day of the week, or when the ``--full`` option is used. Directories using the incremental mode are always backed up on the first day of the week (like a weekly backup), but after that only the files which have changed are actually backed up on a daily basis. In Cedar Backup, incremental backups are not based on date, but are instead based on saved checksums, one for each backed-up file. When a full backup is run, Cedar Backup gathers a checksum value [4]_ for each backed-up file. The next time an incremental backup is run, Cedar Backup checks its list of file/checksum pairs for each file that might be backed up. If the file's checksum value does not match the saved value, or if the file does not appear in the list of file/checksum pairs, then it will be backed up and a new checksum value will be placed into the list. Otherwise, the file will be ignored and the checksum value will be left unchanged. Cedar Backup stores the file/checksum pairs in ``.sha`` files in its working directory, one file per configured collect directory. The mappings in these files are reset at the start of the week or when the ``--full`` option is used. Because these files are used for an entire week, you should never purge the working directory more frequently than once per week. .. _cedar-basic-extensions: Extensions ---------- Imagine that there is a third party developer who understands how to back up a certain kind of database repository. This third party might want to integrate his or her specialized backup into the Cedar Backup process, perhaps thinking of the database backup as a sort of “collect” step. Prior to Cedar Backup version 2, any such integration would have been completely independent of Cedar Backup itself. The “external” backup functionality would have had to maintain its own configuration and would not have had access to any Cedar Backup configuration. Starting with version 2, Cedar Backup allows extensions to the backup process. An extension is an action that isn't part of the standard backup process (i.e. not collect, stage, store or purge), but can be executed by Cedar Backup when properly configured. Extension authors implement an “action process” function with a certain interface, and are allowed to add their own sections to the Cedar Backup configuration file, so that all backup configuration can be centralized. Then, the action process function is associated with an action name which can be executed from the ``cback3`` command line like any other action. Hopefully, as the Cedar Backup user community grows, users will contribute their own extensions back to the community. Well-written general-purpose extensions will be accepted into the official codebase. |note| See :doc:`config` for more information on how extensions are configured, and :doc:`extensions` for details on all of the officially-supported extensions. Developers may be interested in :doc:`extenspec`. ---------- *Previous*: :doc:`preface` • *Next*: :doc:`install` ---------- .. [1] See ``__ .. [2] Some users find this surprising, because extensions are configured with sequence numbers. I did it this way because I felt that running extensions as part of the all action would sometimes result in surprising behavior. I am not planning to change the way this works. .. [3] An ISO image is the standard way of creating a filesystem to be copied to a CD or DVD. It is essentially a “filesystem-within-a-file” and many UNIX operating systems can actually mount ISO image files just like hard drives, floppy disks or actual CDs. See Wikipedia for more information: ``__. .. [4] The checksum is actually an SHA cryptographic hash. See Wikipedia for more information: ``__. .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/commandline.rst0000644000000000000000000007103214567004737016404 0ustar00.. _cedar-commandline: Command Line Tools ================== .. _cedar-commandline-overview: Overview -------- Cedar Backup comes with three command-line programs: ``cback3``, ``cback3-amazons3-sync``, and ``cback3-span``. The ``cback3`` command is the primary command line interface and the only Cedar Backup program that most users will ever need. The ``cback3-amazons3-sync`` tool is used for synchronizing entire directories of files up to an Amazon S3 cloud storage bucket, outside of the normal Cedar Backup process. Users who have a *lot* of data to back up --- more than will fit on a single CD or DVD --- can use the interactive ``cback3-span`` tool to split their data between multiple discs. .. _cedar-commandline-cback3: The ``cback3`` command ---------------------- .. _cedar-commandline-cback3-intro: Introduction ~~~~~~~~~~~~ Cedar Backup's primary command-line interface is the ``cback3`` command. It controls the entire backup process. .. _cedar-commandline-cback3-syntax: Syntax ~~~~~~ The ``cback3`` command has the following syntax: :: Usage: cback3 [switches] action(s) The following switches are accepted: -h, --help Display this usage/help listing -V, --version Display version information -b, --verbose Print verbose output as well as logging to disk -q, --quiet Run quietly (display no output to the screen) -c, --config Path to config file (default: /etc/cback3.conf) -f, --full Perform a full backup, regardless of configuration -M, --managed Include managed clients when executing actions -N, --managed-only Include ONLY managed clients when executing actions -l, --logfile Path to logfile (default: /var/log/cback3.log) -o, --owner Logfile ownership, user:group (default: root:adm) -m, --mode Octal logfile permissions mode (default: 640) -O, --output Record some sub-command (i.e. cdrecord) output to the log -d, --debug Write debugging information to the log (implies --output) -s, --stack Dump a Python stack trace instead of swallowing exceptions -D, --diagnostics Print runtime diagnostics to the screen and exit The following actions may be specified: all Take all normal actions (collect, stage, store, purge) collect Take the collect action stage Take the stage action store Take the store action purge Take the purge action rebuild Rebuild "this week's" disc if possible validate Validate configuration only initialize Initialize media for use with Cedar Backup You may also specify extended actions that have been defined in configuration. You must specify at least one action to take. More than one of the "collect", "stage", "store" or "purge" actions and/or extended actions may be specified in any arbitrary order; they will be executed in a sensible order. The "all", "rebuild", "validate", and "initialize" actions may not be combined with other actions. Note that the all action *only* executes the standard four actions. It never executes any of the configured extensions. [1]_ .. _cedar-commandline-cback3-options: Switches ~~~~~~~~ ``-h``, ``--help`` Display usage/help listing. ``-V``, ``--version`` Display version information. ``-b``, ``--verbose`` Print verbose output to the screen as well writing to the logfile. When this option is enabled, most information that would normally be written to the logfile will also be written to the screen. ``-q``, ``--quiet`` Run quietly (display no output to the screen). ``-c``, ``--config`` Specify the path to an alternate configuration file. The default configuration file is ``/etc/cback3.conf``. ``-f``, ``--full`` Perform a full backup, regardless of configuration. For the collect action, this means that any existing information related to incremental backups will be ignored and rewritten; for the store action, this means that a new disc will be started. ``-M``, ``--managed`` Include managed clients when executing actions. If the action being executed is listed as a managed action for a managed client, execute the action on that client after executing the action locally. ``-N``, ``--managed-only`` Include *only* managed clients when executing actions. If the action being executed is listed as a managed action for a managed client, execute the action on that client --- but *do not* execute the action locally. ``-l``, ``--logfile`` Specify the path to an alternate logfile. The default logfile file is ``/var/log/cback3.log``. ``-o``, ``--owner`` Specify the ownership of the logfile, in the form ``user:group``. The default ownership is ``root:adm``, to match the Debian standard for most logfiles. This value will only be used when creating a new logfile. If the logfile already exists when the ``cback3`` command is executed, it will retain its existing ownership and mode. Only user and group names may be used, not numeric uid and gid values. ``-m``, ``--mode`` Specify the permissions for the logfile, using the numeric mode as in ``chmod(1)``. The default mode is ``0640`` (``-rw-r-----``). This value will only be used when creating a new logfile. If the logfile already exists when the ``cback3`` command is executed, it will retain its existing ownership and mode. ``-O``, ``--output`` Record some sub-command output to the logfile. When this option is enabled, all output from system commands will be logged. This might be useful for debugging or just for reference. ``-d``, ``--debug`` Write debugging information to the logfile. This option produces a high volume of output, and would generally only be needed when debugging a problem. This option implies the ``--output`` option, as well. ``-s``, ``--stack`` Dump a Python stack trace instead of swallowing exceptions. This forces Cedar Backup to dump the entire Python stack trace associated with an error, rather than just propagating last message it received back up to the user interface. Under some circumstances, this is useful information to include along with a bug report. ``-D``, ``--diagnostics`` Display runtime diagnostic information and then exit. This diagnostic information is often useful when filing a bug report. .. _cedar-commandline-cback3-actions: Actions ~~~~~~~ You can find more information about the various actions in :doc:`basic`. In general, you may specify any combination of the *collect*, *stage*, *store* or *purge* actions, and the specified actions will be executed in a sensible order. Or, you can specify one of the *all*, *rebuild*, *validate*, or *initialize* actions (but these actions may not be combined with other actions). If you have configured any Cedar Backup extensions, then the actions associated with those extensions may also be specified on the command line. If you specify any other actions along with an extended action, the actions will be executed in a sensible order per configuration. The *all* action never executes extended actions, however. .. _cedar-commandline-sync: The ``cback3-amazons3-sync`` command ------------------------------------ .. _cedar-commandline-sync-intro: Introduction ~~~~~~~~~~~~ The ``cback3-amazons3-sync`` tool is used for synchronizing entire directories of files up to an Amazon S3 cloud storage bucket, outside of the normal Cedar Backup process. This might be a good option for some types of data, as long as you understand the limitations around retrieving previous versions of objects that get modified or deleted as part of a sync. S3 does support versioning, but it won't be quite as easy to get at those previous versions as with an explicit incremental backup like ``cback3`` provides. Cedar Backup does not provide any tooling that would help you retrieve previous versions. The underlying functionality relies on the `AWS CLI `__ toolset. Before you use this extension, you need to set up your Amazon S3 account and configure AWS CLI as detailed in Amazon's `setup guide `__. The ``aws`` command will be executed as the same user that is executing the ``cback3-amazons3-sync`` command, so make sure you configure it as the proper user. (This is different than the amazons3 extension, which is designed to execute as root and switches over to the configured backup user to execute AWS CLI commands.) .. _cedar-commandline-sync-permissions: Permissons ~~~~~~~~~~ You can use whichever Amazon-supported authentication mechanism you would like when setting up connectivity for the AWS CLI. It's best to set up a separate user in the `IAM Console `__ rather than using your main administrative user. You probably want to lock down this user so that it can only take backup related actions in the AWS infrastructure. One option is to apply the ``AmazonS3FullAccess`` policy, which grants full access to the S3 infrastructure. If you would like to lock down the user even further, this appears to be the minimum set of permissions required for the ``aws s3 sync`` action, written as a JSON policy statement: :: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:PutObject", "s3:PutObjectAcl", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::your-bucket", "arn:aws:s3:::your-bucket/*" ] } ] } In the ``Resource`` section, be sure to list the name of your S3 bucket instead of ``my-bucket``. .. _cedar-commandline-sync-syntax: Syntax ~~~~~~ The ``cback3-amazons3-sync`` command has the following syntax: :: Usage: cback3-amazons3-sync [switches] sourceDir s3bucketUrl Cedar Backup Amazon S3 sync tool. This Cedar Backup utility synchronizes a local directory to an Amazon S3 bucket. After the sync is complete, a validation step is taken. An error is reported if the contents of the bucket do not match the source directory, or if the indicated size for any file differs. This tool is a wrapper over the AWS CLI command-line tool. The following arguments are required: sourceDir The local source directory on disk (must exist) s3BucketUrl The URL to the target Amazon S3 bucket The following switches are accepted: -h, --help Display this usage/help listing -V, --version Display version information -b, --verbose Print verbose output as well as logging to disk -q, --quiet Run quietly (display no output to the screen) -l, --logfile Path to logfile (default: /var/log/cback3.log) -o, --owner Logfile ownership, user:group (default: root:adm) -m, --mode Octal logfile permissions mode (default: 640) -O, --output Record some sub-command (i.e. aws) output to the log -d, --debug Write debugging information to the log (implies --output) -s, --stack Dump Python stack trace instead of swallowing exceptions -D, --diagnostics Print runtime diagnostics to the screen and exit -v, --verifyOnly Only verify the S3 bucket contents, do not make changes -v, --uploadOnly Only upload new data, do not remove files in the S3 bucket -w, --ignoreWarnings Ignore warnings about problematic filename encodings Typical usage would be something like: cback3-amazons3-sync /home/myuser s3://example.com-backup/myuser This will sync the contents of /home/myuser into the indicated bucket. .. _cedar-commandline-sync-options: Switches ~~~~~~~~ ``-h``, ``--help`` Display usage/help listing. ``-V``, ``--version`` Display version information. ``-b``, ``--verbose`` Print verbose output to the screen as well writing to the logfile. When this option is enabled, most information that would normally be written to the logfile will also be written to the screen. ``-q``, ``--quiet`` Run quietly (display no output to the screen). ``-l``, ``--logfile`` Specify the path to an alternate logfile. The default logfile file is ``/var/log/cback3.log``. ``-o``, ``--owner`` Specify the ownership of the logfile, in the form ``user:group``. The default ownership is ``root:adm``, to match the Debian standard for most logfiles. This value will only be used when creating a new logfile. If the logfile already exists when the ``cback3-amazons3-sync`` command is executed, it will retain its existing ownership and mode. Only user and group names may be used, not numeric uid and gid values. ``-m``, ``--mode`` Specify the permissions for the logfile, using the numeric mode as in ``chmod(1)``. The default mode is ``0640`` (``-rw-r-----``). This value will only be used when creating a new logfile. If the logfile already exists when the ``cback3-amazons3-sync`` command is executed, it will retain its existing ownership and mode. ``-O``, ``--output`` Record some sub-command output to the logfile. When this option is enabled, all output from system commands will be logged. This might be useful for debugging or just for reference. ``-d``, ``--debug`` Write debugging information to the logfile. This option produces a high volume of output, and would generally only be needed when debugging a problem. This option implies the ``--output`` option, as well. ``-s``, ``--stack`` Dump a Python stack trace instead of swallowing exceptions. This forces Cedar Backup to dump the entire Python stack trace associated with an error, rather than just propagating last message it received back up to the user interface. Under some circumstances, this is useful information to include along with a bug report. ``-D``, ``--diagnostics`` Display runtime diagnostic information and then exit. This diagnostic information is often useful when filing a bug report. ``-v``, ``--verifyOnly`` Only verify the S3 bucket contents against the directory on disk. Do not make any changes to the S3 bucket or transfer any files. This is intended as a quick check to see whether the sync is up-to-date. Although no files are transferred, the tool will still execute the source filename encoding check, discussed below along with ``--ignoreWarnings``. ``-u``, ``--uploadOnly`` Implement a partial or "upload only" sync, instead of a full synchronization. Normally, synchronization would remove files that exist in S3 but do not exist in the directory on disk. With this flag, new files are uploaded, but no files are removed in S3. ``-w``, ``--ignoreWarnings`` The AWS CLI S3 sync process is very picky about filename encoding. Files that the Linux filesystem handles with no problems can cause problems in S3 if the filename cannot be encoded properly in your configured locale. As of this writing, filenames like this will cause the sync process to abort without transferring all files as expected. To avoid confusion, the ``cback3-amazons3-sync`` tries to guess which files in the source directory will cause problems, and refuses to execute the AWS CLI S3 sync if any problematic files exist. If you'd rather proceed anyway, use ``--ignoreWarnings``. If problematic files are found, then you have basically two options: either correct your locale (i.e. if you have set ``LANG=C``) or rename the file so it can be encoded properly in your locale. The error messages will tell you the expected encoding (from your locale) and the actual detected encoding for the filename. .. _cedar-commandline-cbackspan: The ``cback3-span`` command --------------------------- .. _cedar-commandline-cbackspan-intro: Introduction ~~~~~~~~~~~~ Cedar Backup was designed --- and is still primarily focused --- around weekly backups. Most users who back up more data than fits on a single disc seem to either use Amazon S3 or stop their backup process at the stage step, using Cedar Backup as an easy way to collect data. However, some users have expressed a need to write these large kinds of backups to disc --- if not every day, then at least occassionally. The ``cback3-span`` tool was written to meet those needs. If you have staged more data than fits on a single CD or DVD, you can use ``cback3-span`` to split that data between multiple discs. ``cback3-span`` is not a general-purpose disc-splitting tool. It is a specialized program that requires Cedar Backup configuration to run. All it can do is read Cedar Backup configuration, find any staging directories that have not yet been written to disc, and split the files in those directories between discs. ``cback3-span`` accepts many of the same command-line options as ``cback3``, but *must* be run interactively. It cannot be run from cron. This is intentional. It is intended to be a useful tool, not a new part of the backup process (that is the purpose of an extension). In order to use ``cback3-span``, you must configure your backup such that the largest individual backup file can fit on a single disc. *The command will not split a single file onto more than one disc.* All it can do is split large directories onto multiple discs. Files in those directories will be arbitrarily split up so that space is utilized most efficiently. .. _cedar-commandline-cbackspan-syntax: Syntax ~~~~~~ The ``cback3-span`` command has the following syntax: :: Usage: cback3-span [switches] Cedar Backup 'span' tool. This Cedar Backup utility spans staged data between multiple discs. It is a utility, not an extension, and requires user interaction. The following switches are accepted, mostly to set up underlying Cedar Backup functionality: -h, --help Display this usage/help listing -V, --version Display version information -b, --verbose Print verbose output as well as logging to disk -c, --config Path to config file (default: /etc/cback3.conf) -l, --logfile Path to logfile (default: /var/log/cback3.log) -o, --owner Logfile ownership, user:group (default: root:adm) -m, --mode Octal logfile permissions mode (default: 640) -O, --output Record some sub-command (i.e. cdrecord) output to the log -d, --debug Write debugging information to the log (implies --output) -s, --stack Dump a Python stack trace instead of swallowing exceptions .. _cedar-commandline-cbackspan-options: Switches ~~~~~~~~ ``-h``, ``--help`` Display usage/help listing. ``-V``, ``--version`` Display version information. ``-b``, ``--verbose`` Print verbose output to the screen as well writing to the logfile. When this option is enabled, most information that would normally be written to the logfile will also be written to the screen. ``-c``, ``--config`` Specify the path to an alternate configuration file. The default configuration file is ``/etc/cback3.conf``. ``-l``, ``--logfile`` Specify the path to an alternate logfile. The default logfile file is ``/var/log/cback3.log``. ``-o``, ``--owner`` Specify the ownership of the logfile, in the form ``user:group``. The default ownership is ``root:adm``, to match the Debian standard for most logfiles. This value will only be used when creating a new logfile. If the logfile already exists when the ``cback3`` command is executed, it will retain its existing ownership and mode. Only user and group names may be used, not numeric uid and gid values. ``-m``, ``--mode`` Specify the permissions for the logfile, using the numeric mode as in ``chmod(1)``. The default mode is ``0640`` (``-rw-r-----``). This value will only be used when creating a new logfile. If the logfile already exists when the ``cback3`` command is executed, it will retain its existing ownership and mode. ``-O``, ``--output`` Record some sub-command output to the logfile. When this option is enabled, all output from system commands will be logged. This might be useful for debugging or just for reference. Cedar Backup uses system commands mostly for dealing with the CD/DVD recorder and its media. ``-d``, ``--debug`` Write debugging information to the logfile. This option produces a high volume of output, and would generally only be needed when debugging a problem. This option implies the ``--output`` option, as well. ``-s``, ``--stack`` Dump a Python stack trace instead of swallowing exceptions. This forces Cedar Backup to dump the entire Python stack trace associated with an error, rather than just propagating last message it received back up to the user interface. Under some circumstances, this is useful information to include along with a bug report. .. _cedar-commandline-cbackspan-using: Using ``cback3-span`` ~~~~~~~~~~~~~~~~~~~~~ As discussed above, the ``cback3-span`` is an interactive command. It cannot be run from cron. You can typically use the default answer for most questions. The only two questions that you may not want the default answer for are the fit algorithm and the cushion percentage. The cushion percentage is used by ``cback3-span`` to determine what capacity to shoot for when splitting up your staging directories. A 650 MB disc does not fit fully 650 MB of data. It's usually more like 627 MB of data. The cushion percentage tells ``cback3-span`` how much overhead to reserve for the filesystem. The default of 4% is usually OK, but if you have problems you may need to increase it slightly. The fit algorithm tells ``cback3-span`` how it should determine which items should be placed on each disc. If you don't like the result from one algorithm, you can reject that solution and choose a different algorithm. The four available fit algorithms are: ``worst`` The worst-fit algorithm. The worst-fit algorithm proceeds through a sorted list of items (sorted from smallest to largest) until running out of items or meeting capacity exactly. If capacity is exceeded, the item that caused capacity to be exceeded is thrown away and the next one is tried. The algorithm effectively includes the maximum number of items possible in its search for optimal capacity utilization. It tends to be somewhat slower than either the best-fit or alternate-fit algorithm, probably because on average it has to look at more items before completing. ``best`` The best-fit algorithm. The best-fit algorithm proceeds through a sorted list of items (sorted from largest to smallest) until running out of items or meeting capacity exactly. If capacity is exceeded, the item that caused capacity to be exceeded is thrown away and the next one is tried. The algorithm effectively includes the minimum number of items possible in its search for optimal capacity utilization. For large lists of mixed-size items, it's not unusual to see the algorithm achieve 100% capacity utilization by including fewer than 1% of the items. Probably because it often has to look at fewer of the items before completing, it tends to be a little faster than the worst-fit or alternate-fit algorithms. ``first`` The first-fit algorithm. The first-fit algorithm proceeds through an unsorted list of items until running out of items or meeting capacity exactly. If capacity is exceeded, the item that caused capacity to be exceeded is thrown away and the next one is tried. This algorithm generally performs more poorly than the other algorithms both in terms of capacity utilization and item utilization, but can be as much as an order of magnitude faster on large lists of items because it doesn't require any sorting. ``alternate`` A hybrid algorithm that I call alternate-fit. This algorithm tries to balance small and large items to achieve better end-of-disk performance. Instead of just working one direction through a list, it alternately works from the start and end of a sorted list (sorted from smallest to largest), throwing away any item which causes capacity to be exceeded. The algorithm tends to be slower than the best-fit and first-fit algorithms, and slightly faster than the worst-fit algorithm, probably because of the number of items it considers on average before completing. It often achieves slightly better capacity utilization than the worst-fit algorithm, while including slightly fewer items. .. _cedar-commandline-cbackspan-sample: Sample run ~~~~~~~~~~ Below is a log showing a sample ``cback3-span`` run. :: ================================================ Cedar Backup 'span' tool ================================================ This the Cedar Backup span tool. It is used to split up staging data when that staging data does not fit onto a single disc. This utility operates using Cedar Backup configuration. Configuration specifies which staging directory to look at and which writer device and media type to use. Continue? [Y/n]: === Cedar Backup store configuration looks like this: Source Directory...: /tmp/staging Media Type.........: cdrw-74 Device Type........: cdwriter Device Path........: /dev/cdrom Device SCSI ID.....: None Drive Speed........: None Check Data Flag....: True No Eject Flag......: False Is this OK? [Y/n]: === Please wait, indexing the source directory (this may take a while)... === The following daily staging directories have not yet been written to disc: /tmp/staging/2007/02/07 /tmp/staging/2007/02/08 /tmp/staging/2007/02/09 /tmp/staging/2007/02/10 /tmp/staging/2007/02/11 /tmp/staging/2007/02/12 /tmp/staging/2007/02/13 /tmp/staging/2007/02/14 The total size of the data in these directories is 1.00 GB. Continue? [Y/n]: === Based on configuration, the capacity of your media is 650.00 MB. Since estimates are not perfect and there is some uncertainly in media capacity calculations, it is good to have a "cushion", a percentage of capacity to set aside. The cushion reduces the capacity of your media, so a 1.5% cushion leaves 98.5% remaining. What cushion percentage? [4.00]: === The real capacity, taking into account the 4.00% cushion, is 627.25 MB. It will take at least 2 disc(s) to store your 1.00 GB of data. Continue? [Y/n]: === Which algorithm do you want to use to span your data across multiple discs? The following algorithms are available: first....: The "first-fit" algorithm best.....: The "best-fit" algorithm worst....: The "worst-fit" algorithm alternate: The "alternate-fit" algorithm If you don't like the results you will have a chance to try a different one later. Which algorithm? [worst]: === Please wait, generating file lists (this may take a while)... === Using the "worst-fit" algorithm, Cedar Backup can split your data into 2 discs. Disc 1: 246 files, 615.97 MB, 98.20% utilization Disc 2: 8 files, 412.96 MB, 65.84% utilization Accept this solution? [Y/n]: n === Which algorithm do you want to use to span your data across multiple discs? The following algorithms are available: first....: The "first-fit" algorithm best.....: The "best-fit" algorithm worst....: The "worst-fit" algorithm alternate: The "alternate-fit" algorithm If you don't like the results you will have a chance to try a different one later. Which algorithm? [worst]: alternate === Please wait, generating file lists (this may take a while)... === Using the "alternate-fit" algorithm, Cedar Backup can split your data into 2 discs. Disc 1: 73 files, 627.25 MB, 100.00% utilization Disc 2: 181 files, 401.68 MB, 64.04% utilization Accept this solution? [Y/n]: y === Please place the first disc in your backup device. Press return when ready. === Initializing image... Writing image to disc... ---------- *Previous*: :doc:`install` • *Next*: :doc:`config` ---------- .. [1] Some users find this surprising, because extensions are configured with sequence numbers. I did it this way because I felt that running extensions as part of the all action would sometimes result in “surprising” behavior. Better to be definitive than confusing. .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/config.rst0000644000000000000000000032514614567004737015373 0ustar00.. _cedar-config: Configuration ============= .. _cedar-config-overview: Overview -------- Configuring Cedar Backup is unfortunately somewhat complicated. The good news is that once you get through the initial configuration process, you'll hardly ever have to change anything. Even better, the most typical changes (i.e. adding and removing directories from a backup) are easy. First, familiarize yourself with the concepts in :doc:`basic`. In particular, be sure that you understand the differences between a master and a client. (If you only have one machine, then your machine will act as both a master and a client, and we'll refer to your setup as a pool of one.) Then, install Cedar Backup per the instructions in :doc:`install`. Once everything has been installed, you are ready to begin configuring Cedar Backup. Look over :doc:`commandline` to become familiar with the command line interface. Then, look over *Configuration File Format* (below) and create a configuration file for each peer in your backup pool. To start with, create a very simple configuration file, then expand it later. Decide now whether you will store the configuration file in the standard place (``/etc/cback3.conf``) or in some other location. After you have all of the configuration files in place, configure each of your machines, following the instructions in the appropriate section below (for master, client or pool of one). Since the master and client(s) must communicate over the network, you won't be able to fully configure the master without configuring each client and vice-versa. The instructions are clear on what needs to be done. Cedar Backup has been designed for use on all UNIX-like systems. However, since it was developed on a Debian GNU/Linux system, and because I am a Debian developer, the packaging is prettier and the setup is somewhat simpler on a Debian system than on a system where you install from source. The configuration instructions below have been generalized so they should work well regardless of what platform you are running (i.e. RedHat, Gentoo, FreeBSD, etc.). If instructions vary for a particular platform, you will find a note related to that platform. I am always open to adding more platform-specific hints and notes, so write me if you find problems with these instructions. .. _cedar-config-configfile: Configuration File Format ------------------------- Cedar Backup is configured through an XML [1]_ configuration file, usually called ``/etc/cback3.conf``. The configuration file contains the following sections: reference, options, collect, stage, store, purge and extensions. All configuration files must contain the two general configuration sections, the reference section and the options section. Besides that, administrators need only configure actions they intend to use. For instance, on a client machine, administrators will generally only configure the collect and purge sections, while on a master machine they will have to configure all four action-related sections. [2]_ The extensions section is always optional and can be omitted unless extensions are in use. |note| Even though the Mac OS X (darwin) filesystem is *not* case-sensitive, Cedar Backup configuration *is* generally case-sensitive on that platform, just like on all other platforms. For instance, even though the files “Ken” and “ken” might be the same on the Mac OS X filesystem, an exclusion in Cedar Backup configuration for “ken” will only match the file if it is actually on the filesystem with a lower-case “k” as its first letter. This won't surprise the typical UNIX user, but might surprise someone who's gotten into the “Mac Mindset”. .. _cedar-config-configfile-sample: Sample Configuration File ~~~~~~~~~~~~~~~~~~~~~~~~~ Both the Python source distribution and the Debian package come with a sample configuration file. The Debian package includes its sample in ``/usr/share/doc/cedar-backup3/examples/cback3.conf.sample``. This is a sample configuration file similar to the one provided in the source package. Documentation below provides more information about each of the individual configuration sections. :: Kenneth J. Pronovici 1.3 Sample tuesday /opt/backup/tmp backup group /usr/bin/scp -O -B debian local /opt/backup/collect /opt/backup/collect daily targz .cbignore /etc incr /home/root/.profile weekly /opt/backup/staging /opt/backup/staging cdrw-74 cdwriter /dev/cdrw 0,0,0 4 Y Y Y /opt/backup/stage 7 /opt/backup/collect 0 .. _cedar-config-configfile-reference: Reference Configuration ~~~~~~~~~~~~~~~~~~~~~~~ The reference configuration section contains free-text elements that exist only for reference.. The section itself is required, but the individual elements may be left blank if desired. This is an example reference configuration section: :: Kenneth J. Pronovici Revision 1.3 Sample Yet to be Written Config Tool (tm) The following elements are part of the reference configuration section: ``author`` Author of the configuration file. *Restrictions:* None ``revision`` Revision of the configuration file. *Restrictions:* None ``description`` Description of the configuration file. *Restrictions:* None ``generator`` Tool that generated the configuration file, if any. *Restrictions:* None .. _cedar-config-configfile-options: Options Configuration ~~~~~~~~~~~~~~~~~~~~~ The options configuration section contains configuration options that are not specific to any one action. This is an example options configuration section: :: tuesday /opt/backup/tmp backup backup /usr/bin/scp -O -B /usr/bin/ssh /usr/bin/cback collect, purge cdrecord /opt/local/bin/cdrecord mkisofs /opt/local/bin/mkisofs collect echo "I AM A PRE-ACTION HOOK RELATED TO COLLECT" collect echo "I AM A POST-ACTION HOOK RELATED TO COLLECT" The following elements are part of the options configuration section: ``starting_day`` Day that starts the week. Cedar Backup is built around the idea of weekly backups. The starting day of week is the day that media will be rebuilt from scratch and that incremental backup information will be cleared. *Restrictions:* Must be a day of the week in English, i.e. ``monday``, ``tuesday``, etc. The validation is case-sensitive. ``working_dir`` Working (temporary) directory to use for backups. This directory is used for writing temporary files, such as tar file or ISO filesystem images as they are being built. It is also used to store day-to-day information about incremental backups. The working directory should contain enough free space to hold temporary tar files (on a client) or to build an ISO filesystem image (on a master). *Restrictions:* Must be an absolute path ``backup_user`` Effective user that backups should run as. This user must exist on the machine which is being configured and should not be root (although that restriction is not enforced). This value is also used as the default remote backup user for remote peers. *Restrictions:* Must be non-empty ``backup_group`` Effective group that backups should run as. This group must exist on the machine which is being configured, and should not be root or some other “powerful” group (although that restriction is not enforced). *Restrictions:* Must be non-empty ``rcp_command`` Default rcp-compatible copy command for staging. The rcp command should be the exact command used for remote copies, including any required options. If you are using ``scp``, you should pass it the ``-B`` option, so ``scp`` will not ask for any user input (which could hang the backup). A common example is something like ``/usr/bin/scp -B``. If you are following the instructions in the appendix to restrict SSH access, then use ``/usr/bin/scp -O -B`` instead. This value is used as the default value for all remote peers. Technically, this value is not needed by clients, but we require it for all config files anyway. *Restrictions:* Must be non-empty ``rsh_command`` Default rsh-compatible command to use for remote shells. The rsh command should be the exact command used for remote shells, including any required options. This value is used as the default value for all managed clients. It is optional, because it is only used when executing actions on managed clients. However, each managed client must either be able to read the value from options configuration or must set the value explicitly. *Restrictions:* Must be non-empty ``cback_command`` Default cback-compatible command to use on managed remote clients. The cback command should be the exact command used for for executing ``cback`` on a remote managed client, including any required command-line options. Do *not* list any actions in the command line, and do *not* include the ``--full`` command-line option. This value is used as the default value for all managed clients. It is optional, because it is only used when executing actions on managed clients. However, each managed client must either be able to read the value from options configuration or must set the value explicitly. *Note:* if this command-line is complicated, it is often better to create a simple shell script on the remote host to encapsulate all of the options. Then, just reference the shell script in configuration. *Restrictions:* Must be non-empty ``managed_actions`` Default set of actions that are managed on remote clients. This is a comma-separated list of actions that the master will manage on behalf of remote clients. Typically, it would include only collect-like actions and purge. This value is used as the default value for all managed clients. It is optional, because it is only used when executing actions on managed clients. However, each managed client must either be able to read the value from options configuration or must set the value explicitly. *Restrictions:* Must be non-empty. ``override`` Command to override with a customized path. This is a subsection which contains a command to override with a customized path. This functionality would be used if root's ``$PATH`` does not include a particular required command, or if there is a need to use a version of a command that is different than the one listed on the ``$PATH``. Most users will only use this section when directed to, in order to fix a problem. This section is optional, and can be repeated as many times as necessary. This subsection must contain the following two fields: ``command`` Name of the command to be overridden, i.e. “cdrecord”. *Restrictions:* Must be a non-empty string. ``abs_path`` The absolute path where the overridden command can be found. *Restrictions:* Must be an absolute path. ``pre_action_hook`` Hook configuring a command to be executed before an action. This is a subsection which configures a command to be executed immediately before a named action. It provides a way for administrators to associate their own custom functionality with standard Cedar Backup actions or with arbitrary extensions. This section is optional, and can be repeated as many times as necessary. This subsection must contain the following two fields: ``action`` Name of the Cedar Backup action that the hook is associated with. The action can be a standard backup action (collect, stage, etc.) or can be an extension action. No validation is done to ensure that the configured action actually exists. *Restrictions:* Must be a non-empty string. ``command`` Name of the command to be executed. This item can either specify the path to a shell script of some sort (the recommended approach) or can include a complete shell command. *Note:* if you choose to provide a complete shell command rather than the path to a script, you need to be aware of some limitations of Cedar Backup's command-line parser. You cannot use a subshell (via the :literal:`\`command\`` or ``$(command)`` syntaxes) or any shell variable in your command line. Additionally, the command-line parser only recognizes the double-quote character (``"``) to delimit groupings or strings on the command-line. The bottom line is, you are probably best off writing a shell script of some sort for anything more sophisticated than very simple shell commands. *Restrictions:* Must be a non-empty string. ``post_action_hook`` Hook configuring a command to be executed after an action. This is a subsection which configures a command to be executed immediately after a named action. It provides a way for administrators to associate their own custom functionality with standard Cedar Backup actions or with arbitrary extensions. This section is optional, and can be repeated as many times as necessary. This subsection must contain the following two fields: ``action`` Name of the Cedar Backup action that the hook is associated with. The action can be a standard backup action (collect, stage, etc.) or can be an extension action. No validation is done to ensure that the configured action actually exists. *Restrictions:* Must be a non-empty string. ``command`` Name of the command to be executed. This item can either specify the path to a shell script of some sort (the recommended approach) or can include a complete shell command. *Note:* if you choose to provide a complete shell command rather than the path to a script, you need to be aware of some limitations of Cedar Backup's command-line parser. You cannot use a subshell (via the :literal:`\`command\`` or ``$(command)`` syntaxes) or any shell variable in your command line. Additionally, the command-line parser only recognizes the double-quote character (``"``) to delimit groupings or strings on the command-line. The bottom line is, you are probably best off writing a shell script of some sort for anything more sophisticated than very simple shell commands. *Restrictions:* Must be a non-empty string. .. _cedar-config-configfile-peers: Peers Configuration ~~~~~~~~~~~~~~~~~~~ The peers configuration section contains a list of the peers managed by a master. This section is only required on a master. This is an example peers configuration section: :: machine1 local /opt/backup/collect machine2 remote backup /opt/backup/collect all machine3 remote Y backup /opt/backup/collect /usr/bin/scp /usr/bin/ssh /usr/bin/cback collect, purge The following elements are part of the peers configuration section: ``peer`` (local version) Local client peer in a backup pool. This is a subsection which contains information about a specific local client peer managed by a master. This section can be repeated as many times as is necessary. At least one remote or local peer must be configured. The local peer subsection must contain the following fields: ``name`` Name of the peer, typically a valid hostname. For local peers, this value is only used for reference. However, it is good practice to list the peer's hostname here, for consistency with remote peers. *Restrictions:* Must be non-empty, and unique among all peers. ``type`` Type of this peer. This value identifies the type of the peer. For a local peer, it must always be ``local``. *Restrictions:* Must be ``local``. ``collect_dir`` Collect directory to stage from for this peer. The master will copy all files in this directory into the appropriate staging directory. Since this is a local peer, the directory is assumed to be reachable via normal filesystem operations (i.e. ``cp``). *Restrictions:* Must be an absolute path. ``ignore_failures`` Ignore failure mode for this peer The ignore failure mode indicates whether “not ready to be staged” errors should be ignored for this peer. This option is intended to be used for peers that are up only intermittently, to cut down on the number of error emails received by the Cedar Backup administrator. The "none" mode means that all errors will be reported. This is the default behavior. The "all" mode means to ignore all failures. The "weekly" mode means to ignore failures for a start-of-week or full backup. The "daily" mode means to ignore failures for any backup that is not either a full backup or a start-of-week backup. *Restrictions:* If set, must be one of "none", "all", "daily", or "weekly". ``peer`` (remote version) Remote client peer in a backup pool. This is a subsection which contains information about a specific remote client peer managed by a master. A remote peer is one which can be reached via an rsh-based network call. This section can be repeated as many times as is necessary. At least one remote or local peer must be configured. The remote peer subsection must contain the following fields: ``name`` Hostname of the peer. For remote peers, this must be a valid DNS hostname or IP address which can be resolved during an rsh-based network call. *Restrictions:* Must be non-empty, and unique among all peers. ``type`` Type of this peer. This value identifies the type of the peer. For a remote peer, it must always be ``remote``. *Restrictions:* Must be ``remote``. ``managed`` Indicates whether this peer is managed. A managed peer (or managed client) is a peer for which the master manages all of the backup activites via a remote shell. This field is optional. If it doesn't exist, then ``N`` will be assumed. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``collect_dir`` Collect directory to stage from for this peer. The master will copy all files in this directory into the appropriate staging directory. Since this is a remote peer, the directory is assumed to be reachable via rsh-based network operations (i.e. ``scp`` or the configured rcp command). *Restrictions:* Must be an absolute path. ``ignore_failures`` Ignore failure mode for this peer The ignore failure mode indicates whether “not ready to be staged” errors should be ignored for this peer. This option is intended to be used for peers that are up only intermittently, to cut down on the number of error emails received by the Cedar Backup administrator. The "none" mode means that all errors will be reported. This is the default behavior. The "all" mode means to ignore all failures. The "weekly" mode means to ignore failures for a start-of-week or full backup. The "daily" mode means to ignore failures for any backup that is not either a full backup or a start-of-week backup. *Restrictions:* If set, must be one of "none", "all", "daily", or "weekly". ``backup_user`` Name of backup user on the remote peer. This username will be used when copying files from the remote peer via an rsh-based network connection. This field is optional. if it doesn't exist, the backup will use the default backup user from the options section. *Restrictions:* Must be non-empty. ``rcp_command`` The rcp-compatible copy command for this peer. The rcp command should be the exact command used for remote copies, including any required options. If you are using ``scp``, you should pass it the ``-B`` option, so ``scp`` will not ask for any user input (which could hang the backup). A common example is something like ``/usr/bin/scp -B``. If you are following the instructions in the appendix to restrict SSH access, then use ``/usr/bin/scp -O -B`` instead. This field is optional. if it doesn't exist, the backup will use the default rcp command from the options section. *Restrictions:* Must be non-empty. ``rsh_command`` The rsh-compatible command for this peer. The rsh command should be the exact command used for remote shells, including any required options. This value only applies if the peer is managed. This field is optional. if it doesn't exist, the backup will use the default rsh command from the options section. *Restrictions:* Must be non-empty ``cback_command`` The cback-compatible command for this peer. The cback command should be the exact command used for for executing cback on the peer as part of a managed backup. This value must include any required command-line options. Do *not* list any actions in the command line, and do *not* include the ``--full`` command-line option. This value only applies if the peer is managed. This field is optional. if it doesn't exist, the backup will use the default cback command from the options section. *Note:* if this command-line is complicated, it is often better to create a simple shell script on the remote host to encapsulate all of the options. Then, just reference the shell script in configuration. *Restrictions:* Must be non-empty ``managed_actions`` Set of actions that are managed for this peer. This is a comma-separated list of actions that the master will manage on behalf this peer. Typically, it would include only collect-like actions and purge. This value only applies if the peer is managed. This field is optional. if it doesn't exist, the backup will use the default list of managed actions from the options section. *Restrictions:* Must be non-empty. .. _cedar-config-configfile-collect: Collect Configuration ~~~~~~~~~~~~~~~~~~~~~ The collect configuration section contains configuration options related the the collect action. This section contains a variable number of elements, including an optional exclusion section and a repeating subsection used to specify which directories and/or files to collect. You can also configure an ignore indicator file, which lets users mark their own directories as not backed up. Sometimes, it's not very convenient to list directories one by one in the Cedar Backup configuration file. For instance, when backing up your home directory, you often exclude as many directories as you include. The ignore file mechanism can be of some help, but it still isn't very convenient if there are a lot of directories to ignore (or if new directories pop up all of the time). In this situation, one option is to use a link farm rather than listing all of the directories in configuration. A link farm is a directory that contains nothing but a set of soft links to other files and directories. Normally, Cedar Backup does not follow soft links, but you can override this behavior for individual directories using the ``link_depth`` and ``dereference`` options (see below). When using a link farm, you still have to deal with each backed-up directory individually, but you don't have to modify configuration. Some users find that this works better for them. In order to actually execute the collect action, you must have configured at least one collect directory or one collect file. However, if you are only including collect configuration for use by an extension, then it's OK to leave out these sections. The validation will take place only when the collect action is executed. This is an example collect configuration section: :: /opt/backup/collect daily targz .cbignore /etc .*\.conf /home/root/.profile /etc /var/log incr /opt weekly /opt/large backup .*tmp The following elements are part of the collect configuration section: ``collect_dir`` Directory to collect files into. On a client, this is the directory which tarfiles for individual collect directories are written into. The master then stages files from this directory into its own staging directory. This field is always required. It must contain enough free space to collect all of the backed-up files on the machine in a compressed form. *Restrictions:* Must be an absolute path ``collect_mode`` Default collect mode. The collect mode describes how frequently a directory is backed up. See :doc:`basic` for more information. This value is the collect mode that will be used by default during the collect process. Individual collect directories (below) may override this value. If *all* individual directories provide their own value, then this default value may be omitted from configuration. *Note:* if your backup device does not suppport multisession discs, then you should probably use the ``daily`` collect mode to avoid losing data. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``archive_mode`` Default archive mode for collect files. The archive mode maps to the way that a backup file is stored. A value ``tar`` means just a tarfile (``file.tar``); a value ``targz`` means a gzipped tarfile (``file.tar.gz``); and a value ``tarbz2`` means a bzipped tarfile (``file.tar.bz2``) This value is the archive mode that will be used by default during the collect process. Individual collect directories (below) may override this value. If *all* individual directories provide their own value, then this default value may be omitted from configuration. *Restrictions:* Must be one of ``tar``, ``targz`` or ``tarbz2``. ``ignore_file`` Default ignore file name. The ignore file is an indicator file. If it exists in a given directory, then that directory will be recursively excluded from the backup as if it were explicitly excluded in configuration. The ignore file provides a way for individual users (who might not have access to Cedar Backup configuration) to control which of their own directories get backed up. For instance, users with a ``~/tmp`` directory might not want it backed up. If they create an ignore file in their directory (e.g. ``~/tmp/.cbignore``), then Cedar Backup will ignore it. This value is the ignore file name that will be used by default during the collect process. Individual collect directories (below) may override this value. If *all* individual directories provide their own value, then this default value may be omitted from configuration. *Restrictions:* Must be non-empty ``recursion_level`` Recursion level to use when collecting directories. This is an integer value that Cedar Backup will consider when generating archive files for a configured collect directory. Normally, Cedar Backup generates one archive file per collect directory. So, if you collect ``/etc`` you get ``etc.tar.gz``. Most of the time, this is what you want. However, you may sometimes wish to generate multiple archive files for a single collect directory. The most obvious example is for ``/home``. By default, Cedar Backup will generate ``home.tar.gz``. If instead, you want one archive file per home directory you can set a recursion level of ``1``. Cedar Backup will generate ``home-user1.tar.gz``, ``home-user2.tar.gz``, etc. Higher recursion levels (``2``, ``3``, etc.) are legal, and it doesn't matter if the configured recursion level is deeper than the directory tree that is being collected. You can use a negative recursion level (like ``-1``) to specify an infinite level of recursion. This will exhaust the tree in the same way as if the recursion level is set too high. This field is optional. if it doesn't exist, the backup will use the default recursion level of zero. *Restrictions:* Must be an integer. ``exclude`` List of paths or patterns to exclude from the backup. This is a subsection which contains a set of absolute paths and patterns to be excluded across all configured directories. For a given directory, the set of absolute paths and patterns to exclude is built from this list and any list that exists on the directory itself. Directories *cannot* override or remove entries that are in this list, however. This section is optional, and if it exists can also be empty. The exclude subsection can contain one or more of each of the following fields: ``abs_path`` An absolute path to be recursively excluded from the backup. If a directory is excluded, then all of its children are also recursively excluded. For instance, a value ``/var/log/apache`` would exclude any files within ``/var/log/apache`` as well as files within other directories under ``/var/log/apache``. This field can be repeated as many times as is necessary. *Restrictions:* Must be an absolute path. ``pattern`` A pattern to be recursively excluded from the backup. The pattern must be a Python regular expression. [3]_ It is assumed to be bounded at front and back by the beginning and end of the string (i.e. it is treated as if it begins with ``^`` and ends with ``$``). If the pattern causes a directory to be excluded, then all of the children of that directory are also recursively excluded. For instance, a value ``.*apache.*`` might match the ``/var/log/apache`` directory. This would exclude any files within ``/var/log/apache`` as well as files within other directories under ``/var/log/apache``. This field can be repeated as many times as is necessary. *Restrictions:* Must be non-empty ``file`` A file to be collected. This is a subsection which contains information about a specific file to be collected (backed up). This section can be repeated as many times as is necessary. At least one collect directory or collect file must be configured when the collect action is executed. The collect file subsection contains the following fields: ``abs_path`` Absolute path of the file to collect. *Restrictions:* Must be an absolute path. ``collect_mode`` Collect mode for this file The collect mode describes how frequently a file is backed up. See :doc:`basic` for more information. This field is optional. If it doesn't exist, the backup will use the default collect mode. *Note:* if your backup device does not suppport multisession discs, then you should probably confine yourself to the ``daily`` collect mode, to avoid losing data. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``archive_mode`` Archive mode for this file. The archive mode maps to the way that a backup file is stored. A value ``tar`` means just a tarfile (``file.tar``); a value ``targz`` means a gzipped tarfile (``file.tar.gz``); and a value ``tarbz2`` means a bzipped tarfile (``file.tar.bz2``) This field is optional. if it doesn't exist, the backup will use the default archive mode. *Restrictions:* Must be one of ``tar``, ``targz`` or ``tarbz2``. ``dir`` A directory to be collected. This is a subsection which contains information about a specific directory to be collected (backed up). This section can be repeated as many times as is necessary. At least one collect directory or collect file must be configured when the collect action is executed. The collect directory subsection contains the following fields: ``abs_path`` Absolute path of the directory to collect. The path may be either a directory, a soft link to a directory, or a hard link to a directory. All three are treated the same at this level. The contents of the directory will be recursively collected. The backup will contain all of the files in the directory, as well as the contents of all of the subdirectories within the directory, etc. Soft links *within* the directory are treated as files, i.e. they are copied verbatim (as a link) and their contents are not backed up. *Restrictions:* Must be an absolute path. ``collect_mode`` Collect mode for this directory The collect mode describes how frequently a directory is backed up. See :doc:`basic` for more information. This field is optional. If it doesn't exist, the backup will use the default collect mode. *Note:* if your backup device does not suppport multisession discs, then you should probably confine yourself to the ``daily`` collect mode, to avoid losing data. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``archive_mode`` Archive mode for this directory. The archive mode maps to the way that a backup file is stored. A value ``tar`` means just a tarfile (``file.tar``); a value ``targz`` means a gzipped tarfile (``file.tar.gz``); and a value ``tarbz2`` means a bzipped tarfile (``file.tar.bz2``) This field is optional. if it doesn't exist, the backup will use the default archive mode. *Restrictions:* Must be one of ``tar``, ``targz`` or ``tarbz2``. ``ignore_file`` Ignore file name for this directory. The ignore file is an indicator file. If it exists in a given directory, then that directory will be recursively excluded from the backup as if it were explicitly excluded in configuration. The ignore file provides a way for individual users (who might not have access to Cedar Backup configuration) to control which of their own directories get backed up. For instance, users with a ``~/tmp`` directory might not want it backed up. If they create an ignore file in their directory (e.g. ``~/tmp/.cbignore``), then Cedar Backup will ignore it. This field is optional. If it doesn't exist, the backup will use the default ignore file name. *Restrictions:* Must be non-empty ``link_depth`` Link depth value to use for this directory. The link depth is maximum depth of the tree at which soft links should be followed. So, a depth of 0 does not follow any soft links within the collect directory, a depth of 1 follows only links immediately within the collect directory, a depth of 2 follows the links at the next level down, etc. This field is optional. If it doesn't exist, the backup will assume a value of zero, meaning that soft links within the collect directory will never be followed. *Restrictions:* If set, must be an integer GE 0. ``dereference`` Whether to dereference soft links. If this flag is set, links that are being followed will be dereferenced before being added to the backup. The link will be added (as a link), and then the directory or file that the link points at will be added as well. This value only applies to a directory where soft links are being followed (per the ``link_depth`` configuration option). It never applies to a configured collect directory itself, only to other directories within the collect directory. This field is optional. If it doesn't exist, the backup will assume that links should never be dereferenced. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``exclude`` List of paths or patterns to exclude from the backup. This is a subsection which contains a set of paths and patterns to be excluded within this collect directory. This list is combined with the program-wide list to build a complete list for the directory. This section is entirely optional, and if it exists can also be empty. The exclude subsection can contain one or more of each of the following fields: ``abs_path`` An absolute path to be recursively excluded from the backup. If a directory is excluded, then all of its children are also recursively excluded. For instance, a value ``/var/log/apache`` would exclude any files within ``/var/log/apache`` as well as files within other directories under ``/var/log/apache``. This field can be repeated as many times as is necessary. *Restrictions:* Must be an absolute path. ``rel_path`` A relative path to be recursively excluded from the backup. The path is assumed to be relative to the collect directory itself. For instance, if the configured directory is ``/opt/web`` a configured relative path of ``something/else`` would exclude the path ``/opt/web/something/else``. If a directory is excluded, then all of its children are also recursively excluded. For instance, a value ``something/else`` would exclude any files within ``something/else`` as well as files within other directories under ``something/else``. This field can be repeated as many times as is necessary. *Restrictions:* Must be non-empty. ``pattern`` A pattern to be excluded from the backup. The pattern must be a Python regular expression. It is assumed to be bounded at front and back by the beginning and end of the string (i.e. it is treated as if it begins with ``^`` and ends with ``$``). If the pattern causes a directory to be excluded, then all of the children of that directory are also recursively excluded. For instance, a value ``.*apache.*`` might match the ``/var/log/apache`` directory. This would exclude any files within ``/var/log/apache`` as well as files within other directories under ``/var/log/apache``. This field can be repeated as many times as is necessary. *Restrictions:* Must be non-empty .. _cedar-config-configfile-stage: Stage Configuration ~~~~~~~~~~~~~~~~~~~ The stage configuration section contains configuration options related the the stage action. The section indicates where date from peers can be staged to. This section can also (optionally) override the list of peers so that not all peers are staged. If you provide *any* peers in this section, then the list of peers here completely replaces the list of peers in the peers configuration section for the purposes of staging. This is an example stage configuration section for the simple case where the list of peers is taken from peers configuration: :: /opt/backup/stage This is an example stage configuration section that overrides the default list of peers: :: /opt/backup/stage machine1 local /opt/backup/collect machine2 remote backup /opt/backup/collect The following elements are part of the stage configuration section: ``staging_dir`` Directory to stage files into. This is the directory into which the master stages collected data from each of the clients. Within the staging directory, data is staged into date-based directories by peer name. For instance, peer “daystrom” backed up on 19 Feb 2005 would be staged into something like ``2005/02/19/daystrom`` relative to the staging directory itself. This field is always required. The directory must contain enough free space to stage all of the files collected from all of the various machines in a backup pool. Many administrators set up purging to keep staging directories around for a week or more, which requires even more space. *Restrictions:* Must be an absolute path ``peer`` (local version) Local client peer in a backup pool. This is a subsection which contains information about a specific local client peer to be staged (backed up). A local peer is one whose collect directory can be reached without requiring any rsh-based network calls. It is possible that a remote peer might be staged as a local peer if its collect directory is mounted to the master via NFS, AFS or some other method. This section can be repeated as many times as is necessary. At least one remote or local peer must be configured. *Remember*, if you provide *any* local or remote peer in staging configuration, the global peer configuration is completely replaced by the staging peer configuration. The local peer subsection must contain the following fields: ``name`` Name of the peer, typically a valid hostname. For local peers, this value is only used for reference. However, it is good practice to list the peer's hostname here, for consistency with remote peers. *Restrictions:* Must be non-empty, and unique among all peers. ``type`` Type of this peer. This value identifies the type of the peer. For a local peer, it must always be ``local``. *Restrictions:* Must be ``local``. ``collect_dir`` Collect directory to stage from for this peer. The master will copy all files in this directory into the appropriate staging directory. Since this is a local peer, the directory is assumed to be reachable via normal filesystem operations (i.e. ``cp``). *Restrictions:* Must be an absolute path. ``peer`` (remote version) Remote client peer in a backup pool. This is a subsection which contains information about a specific remote client peer to be staged (backed up). A remote peer is one whose collect directory can only be reached via an rsh-based network call. This section can be repeated as many times as is necessary. At least one remote or local peer must be configured. *Remember*, if you provide *any* local or remote peer in staging configuration, the global peer configuration is completely replaced by the staging peer configuration. The remote peer subsection must contain the following fields: ``name`` Hostname of the peer. For remote peers, this must be a valid DNS hostname or IP address which can be resolved during an rsh-based network call. *Restrictions:* Must be non-empty, and unique among all peers. ``type`` Type of this peer. This value identifies the type of the peer. For a remote peer, it must always be ``remote``. *Restrictions:* Must be ``remote``. ``collect_dir`` Collect directory to stage from for this peer. The master will copy all files in this directory into the appropriate staging directory. Since this is a remote peer, the directory is assumed to be reachable via rsh-based network operations (i.e. ``scp`` or the configured rcp command). *Restrictions:* Must be an absolute path. ``backup_user`` Name of backup user on the remote peer. This username will be used when copying files from the remote peer via an rsh-based network connection. This field is optional. if it doesn't exist, the backup will use the default backup user from the options section. *Restrictions:* Must be non-empty. ``rcp_command`` The rcp-compatible copy command for this peer. The rcp command should be the exact command used for remote copies, including any required options. If you are using ``scp``, you should pass it the ``-B`` option, so ``scp`` will not ask for any user input (which could hang the backup). A common example is something like ``/usr/bin/scp -B``. If you are following the instructions in the appendix to restrict SSH access, then use ``/usr/bin/scp -O -B`` instead. This field is optional. if it doesn't exist, the backup will use the default rcp command from the options section. *Restrictions:* Must be non-empty. .. _cedar-config-configfile-store: Store Configuration ~~~~~~~~~~~~~~~~~~~ The store configuration section contains configuration options related the the store action. This section contains several optional fields. Most fields control the way media is written using the writer device. This is an example store configuration section: :: /opt/backup/stage cdrw-74 cdwriter /dev/cdrw 0,0,0 4 Y Y Y N 15 2 weekly 1.3 The following elements are part of the store configuration section: ``source_dir`` Directory whose contents should be written to media. This directory *must* be a Cedar Backup staging directory, as configured in the staging configuration section. Only certain data from that directory (typically, data from the current day) will be written to disc. *Restrictions:* Must be an absolute path ``device_type`` Type of the device used to write the media. This field controls which type of writer device will be used by Cedar Backup. Currently, Cedar Backup supports CD writers (``cdwriter``) and DVD writers (``dvdwriter``). This field is optional. If it doesn't exist, the ``cdwriter`` device type is assumed. *Restrictions:* If set, must be either ``cdwriter`` or ``dvdwriter``. ``media_type`` Type of the media in the device. Unless you want to throw away a backup disc every week, you are probably best off using rewritable media. You must choose a media type that is appropriate for the device type you chose above. For more information on media types, see :doc:`basic`. *Restrictions:* Must be one of ``cdr-74``, ``cdrw-74``, ``cdr-80`` or ``cdrw-80`` if device type is ``cdwriter``; or one of ``dvd+r`` or ``dvd+rw`` if device type is ``dvdwriter``. ``target_device`` Filesystem device name for writer device. This value is required for both CD writers and DVD writers. This is the UNIX device name for the writer drive, for instance ``/dev/scd0`` or a symlink like ``/dev/cdrw``. In some cases, this device name is used to directly write to media. This is true all of the time for DVD writers, and is true for CD writers when a SCSI id (see below) has not been specified. Besides this, the device name is also needed in order to do several pre-write checks (such as whether the device might already be mounted) as well as the post-write consistency check, if enabled. *Note:* some users have reported intermittent problems when using a symlink as the target device on Linux, especially with DVD media. If you experience problems, try using the real device name rather than the symlink. *Restrictions:* Must be an absolute path. ``target_scsi_id`` SCSI id for the writer device. This value is optional for CD writers and is ignored for DVD writers. If you have configured your CD writer hardware to work through the normal filesystem device path, then you can leave this parameter unset. Cedar Backup will just use the target device (above) when talking to ``cdrecord``. Otherwise, if you have SCSI CD writer hardware or you have configured your non-SCSI hardware to operate like a SCSI device, then you need to provide Cedar Backup with a SCSI id it can use when talking with ``cdrecord``. For the purposes of Cedar Backup, a valid SCSI identifier must either be in the standard SCSI identifier form ``scsibus,target,lun`` or in the specialized-method form ``:scsibus,target,lun``. An example of a standard SCSI identifier is ``1,6,2``. Today, the two most common examples of the specialized-method form are ``ATA:scsibus,target,lun`` and ``ATAPI:scsibus,target,lun``, but you may occassionally see other values (like ``OLDATAPI`` in some forks of ``cdrecord``). See *Configuring your Writer Device* for more information on writer devices and how they are configured. *Restrictions:* If set, must be a valid SCSI identifier. ``drive_speed`` Speed of the drive, i.e. ``2`` for a 2x device. This field is optional. If it doesn't exist, the underlying device-related functionality will use the default drive speed. For DVD writers, it is best to leave this value unset, so ``growisofs`` can pick an appropriate speed. For CD writers, since media can be speed-sensitive, it is probably best to set a sensible value based on your specific writer and media. *Restrictions:* If set, must be an integer GE 1. ``check_data`` Whether the media should be validated. This field indicates whether a resulting image on the media should be validated after the write completes, by running a consistency check against it. If this check is enabled, the contents of the staging directory are directly compared to the media, and an error is reported if there is a mismatch. Practice shows that some drives can encounter an error when writing a multisession disc, but not report any problems. This consistency check allows us to catch the problem. By default, the consistency check is disabled, but most users should choose to enable it unless they have a good reason not to. This field is optional. If it doesn't exist, then ``N`` will be assumed. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``check_media`` Whether the media should be checked before writing to it. By default, Cedar Backup does not check its media before writing to it. It will write to any media in the backup device. If you set this flag to Y, Cedar Backup will make sure that the media has been initialized before writing to it. (Rewritable media is initialized using the initialize action.) If the configured media is not rewritable (like CD-R), then this behavior is modified slightly. For this kind of media, the check passes either if the media has been initialized *or* if the media appears unused. This field is optional. If it doesn't exist, then ``N`` will be assumed. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``warn_midnite`` Whether to generate warnings for crossing midnite. This field indicates whether warnings should be generated if the store operation has to cross a midnite boundary in order to find data to write to disc. For instance, a warning would be generated if valid store data was only found in the day before or day after the current day. Configuration for some users is such that the store operation will always cross a midnite boundary, so they will not care about this warning. Other users will expect to never cross a boundary, and want to be notified that something “strange” might have happened. This field is optional. If it doesn't exist, then ``N`` will be assumed. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``no_eject`` Indicates that the writer device should not be ejected. Under some circumstances, Cedar Backup ejects (opens and closes) the writer device. This is done because some writer devices need to re-load the media before noticing a media state change (like a new session). For most writer devices this is safe, because they have a tray that can be opened and closed. If your writer device does not have a tray *and* Cedar Backup does not properly detect this, then set this flag. Cedar Backup will not ever issue an eject command to your writer. *Note:* this could cause problems with your backup. For instance, with many writers, the check data step may fail if the media is not reloaded first. If this happens to you, you may need to get a different writer device. This field is optional. If it doesn't exist, then ``N`` will be assumed. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``refresh_media_delay`` Number of seconds to delay after refreshing media This field is optional. If it doesn't exist, no delay will occur. Some devices seem to take a little while to stablize after refreshing the media (i.e. closing and opening the tray). During this period, operations on the media may fail. If your device behaves like this, you can try setting a delay of 10-15 seconds. *Restrictions:* If set, must be an integer GE 1. ``eject_delay`` Number of seconds to delay after ejecting the tray This field is optional. If it doesn't exist, no delay will occur. If your system seems to have problems opening and closing the tray, one possibility is that the open/close sequence is happening too quickly --- either the tray isn't fully open when Cedar Backup tries to close it, or it doesn't report being open. To work around that problem, set an eject delay of a few seconds. *Restrictions:* If set, must be an integer GE 1. ``blank_behavior`` Optimized blanking strategy. For more information about Cedar Backup's optimized blanking strategy, see :doc:`config`. This entire configuration section is optional. However, if you choose to provide it, you must configure both a blanking mode and a blanking factor. ``blank_mode`` Blanking mode. *Restrictions:*\ Must be one of "daily" or "weekly". ``blank_factor`` Blanking factor. *Restrictions:*\ Must be a floating point number GE 0. .. _cedar-config-configfile-purge: Purge Configuration ~~~~~~~~~~~~~~~~~~~ The purge configuration section contains configuration options related the the purge action. This section contains a set of directories to be purged, along with information about the schedule at which they should be purged. Typically, Cedar Backup should be configured to purge collect directories daily (retain days of ``0``). If you are tight on space, staging directories can also be purged daily. However, if you have space to spare, you should consider purging about once per week. That way, if your backup media is damaged, you will be able to recreate the week's backup using the rebuild action. You should also purge the working directory periodically, once every few weeks or once per month. This way, if any unneeded files are left around, perhaps because a backup was interrupted or because configuration changed, they will eventually be removed. *The working directory should not be purged any more frequently than once per week, otherwise you will risk destroying data used for incremental backups.* This is an example purge configuration section: :: /opt/backup/stage 7 /opt/backup/collect 0 The following elements are part of the purge configuration section: ``dir`` A directory to purge within. This is a subsection which contains information about a specific directory to purge within. This section can be repeated as many times as is necessary. At least one purge directory must be configured. The purge directory subsection contains the following fields: ``abs_path`` Absolute path of the directory to purge within. The contents of the directory will be purged based on age. The purge will remove any files that were last modified more than “retain days” days ago. Empty directories will also eventually be removed. The purge directory itself will never be removed. The path may be either a directory, a soft link to a directory, or a hard link to a directory. Soft links *within* the directory (if any) are treated as files. *Restrictions:* Must be an absolute path. ``retain_days`` Number of days to retain old files. Once it has been more than this many days since a file was last modified, it is a candidate for removal. *Restrictions:* Must be an integer GE 0. .. _cedar-config-configfile-extensions: Extensions Configuration ~~~~~~~~~~~~~~~~~~~~~~~~ The extensions configuration section is used to configure third-party extensions to Cedar Backup. If you don't intend to use any extensions, or don't know what extensions are, then you can safely leave this section out of your configuration file. It is optional. Extensions configuration is used to specify “extended actions” implemented by code external to Cedar Backup. An administrator can use this section to map command-line Cedar Backup actions to third-party extension functions. Each extended action has a name, which is mapped to a Python function within a particular module. Each action also has an index associated with it. This index is used to properly order execution when more than one action is specified on the command line. The standard actions have predefined indexes, and extended actions are interleaved into the normal order of execution using those indexes. The collect action has index 100, the stage index has action 200, the store action has index 300 and the purge action has index 400. |warning| Extended actions should always be configured to run *before* the standard action they are associated with. This is because of the way indicator files are used in Cedar Backup. For instance, the staging process considers the collect action to be complete for a peer if the file ``cback.collect`` can be found in that peer's collect directory. If you were to run the standard collect action before your other collect-like actions, the indicator file would be written after the collect action completes but *before* all of the other actions even run. Because of this, there's a chance the stage process might back up the collect directory before the entire set of collect-like actions have completed --- and you would get no warning about this in your email! So, imagine that a third-party developer provided a Cedar Backup extension to back up a certain kind of database repository, and you wanted to map that extension to the “database” command-line action. You have been told that this function is called “foo.bar()”. You think of this backup as a “collect” kind of action, so you want it to be performed immediately before the collect action. To configure this extension, you would list an action with a name “database”, a module “foo”, a function name “bar” and an index of “99”. This is how the hypothetical action would be configured: :: database foo bar 99 The following elements are part of the extensions configuration section: ``action`` This is a subsection that contains configuration related to a single extended action. This section can be repeated as many times as is necessary. The action subsection contains the following fields: ``name`` Name of the extended action. *Restrictions:* Must be a non-empty string consisting of only lower-case letters and digits. ``module`` Name of the Python module associated with the extension function. *Restrictions:* Must be a non-empty string and a valid Python identifier. ``function`` Name of the Python extension function within the module. *Restrictions:* Must be a non-empty string and a valid Python identifier. ``index`` Index of action, for execution ordering. *Restrictions:* Must be an integer GE 0. .. _cedar-config-poolofone: Setting up a Pool of One ------------------------ Cedar Backup has been designed primarily for situations where there is a single master and a set of other clients that the master interacts with. However, it will just as easily work for a single machine (a backup pool of one). Once you complete all of these configuration steps, your backups will run as scheduled out of cron. Any errors that occur will be reported in daily emails to your root user (or the user that receives root's email). If you don't receive any emails, then you know your backup worked. *Note:* all of these configuration steps should be run as the root user, unless otherwise indicated. |tip| This setup procedure discusses how to set up Cedar Backup in the “normal case” for a pool of one. If you would like to modify the way Cedar Backup works (for instance, by ignoring the store stage and just letting your backup sit in a staging directory), you can do that. You'll just have to modify the procedure below based on information in the remainder of the manual. Step 1: Decide when you will run your backup. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are four parts to a Cedar Backup run: collect, stage, store and purge. The usual way of setting off these steps is through a set of cron jobs. Although you won't create your cron jobs just yet, you should decide now when you will run your backup so you are prepared for later. Backing up large directories and creating ISO filesystem images can be intensive operations, and could slow your computer down significantly. Choose a backup time that will not interfere with normal use of your computer. Usually, you will want the backup to occur every day, but it is possible to configure cron to execute the backup only one day per week, three days per week, etc. |warning| Because of the way Cedar Backup works, you must ensure that your backup *always* runs on the first day of your configured week. This is because Cedar Backup will only clear incremental backup information and re-initialize your media when running on the first day of the week. If you skip running Cedar Backup on the first day of the week, your backups will likely be “confused” until the next week begins, or until you re-run the backup using the ``--full`` flag. Step 2: Make sure email works. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cedar Backup relies on email for problem notification. This notification works through the magic of cron. Cron will email any output from each job it executes to the user associated with the job. Since by default Cedar Backup only writes output to the terminal if errors occur, this ensures that notification emails will only be sent out if errors occur. In order to receive problem notifications, you must make sure that email works for the user which is running the Cedar Backup cron jobs (typically root). Refer to your distribution's documentation for information on how to configure email on your system. Note that you may prefer to configure root's email to forward to some other user, so you do not need to check the root user's mail in order to see Cedar Backup errors. Step 3: Configure your writer device. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before using Cedar Backup, your writer device must be properly configured. If you have configured your CD/DVD writer hardware to work through the normal filesystem device path, then you just need to know the path to the device on disk (something like ``/dev/cdrw``). Cedar Backup will use the this device path both when talking to a command like ``cdrecord`` and when doing filesystem operations like running media validation. Your other option is to configure your CD writer hardware like a SCSI device (either because it *is* a SCSI device or because you are using some sort of interface that makes it look like one). In this case, Cedar Backup will use the SCSI id when talking to ``cdrecord`` and the device path when running filesystem operations. See :doc:`config` for more information on writer devices and how they are configured. |note| There is no need to set up your CD/DVD device if you have decided not to execute the store action. Due to the underlying utilities that Cedar Backup uses, the SCSI id may only be used for CD writers, *not* DVD writers. Step 4: Configure your backup user. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Choose a user to be used for backups. Some platforms may come with a “ready made” backup user. For other platforms, you may have to create a user yourself. You may choose any id you like, but a descriptive name such as ``backup`` or ``cback`` is a good choice. See your distribution's documentation for information on how to add a user. |note| Standard Debian systems come with a user named ``backup``. You may choose to stay with this user or create another one. Step 5: Create your backup tree. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cedar Backup requires a backup directory tree on disk. This directory tree must be roughly three times as big as the amount of data that will be backed up on a nightly basis, to allow for the data to be collected, staged, and then placed into an ISO filesystem image on disk. (This is one disadvantage to using Cedar Backup in single-machine pools, but in this day of really large hard drives, it might not be an issue.) Note that if you elect not to purge the staging directory every night, you will need even more space. You should create a collect directory, a staging directory and a working (temporary) directory. One recommended layout is this: :: /opt/ backup/ collect/ stage/ tmp/ If you will be backing up sensitive information (i.e. password files), it is recommended that these directories be owned by the backup user (whatever you named it), with permissions ``700``. |note| You don't have to use ``/opt`` as the root of your directory structure. Use anything you would like. I use ``/opt`` because it is my “dumping ground” for filesystems that Debian does not manage. Some users have requested that the Debian packages set up a more “standard” location for backups right out-of-the-box. I have resisted doing this because it's difficult to choose an appropriate backup location from within the package. If you would prefer, you can create the backup directory structure within some existing Debian directory such as ``/var/backups`` or ``/var/tmp``. Step 6: Create the Cedar Backup configuration file. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Following the instructions in *Configuration File Format* (above) create a configuration file for your machine. Since you are working with a pool of one, you must configure all four action-specific sections: collect, stage, store and purge. The usual location for the Cedar Backup config file is ``/etc/cback3.conf``. If you change the location, make sure you edit your cronjobs (below) to point the ``cback3`` script at the correct config file (using the ``--config`` option). |warning| Configuration files should always be writable only by root (or by the file owner, if the owner is not root). If you intend to place confidential information into the Cedar Backup configuration file, make sure that you set the filesystem permissions on the file appropriately. For instance, if you configure any extensions that require passwords or other similar information, you should make the file readable only to root or to the file owner (if the owner is not root). Step 7: Validate the Cedar Backup configuration file. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the command ``cback3 validate`` to validate your configuration file. This command checks that the configuration file can be found and parsed, and also checks for typical configuration problems, such as invalid CD/DVD device entries. *Note:* the most common cause of configuration problems is in not closing XML tags properly. Any XML tag that is “opened” must be “closed” appropriately. Step 8: Test your backup. ~~~~~~~~~~~~~~~~~~~~~~~~~ Place a valid CD/DVD disc in your drive, and then use the command ``cback3 --full all``. You should execute this command as root. If the command completes with no output, then the backup was run successfully. Just to be sure that everything worked properly, check the logfile (``/var/log/cback3.log``) for errors and also mount the CD/DVD disc to be sure it can be read. *If Cedar Backup ever completes “normally” but the disc that is created is not usable, please report this as a bug. To be safe, always enable the consistency check option in the store configuration section.* [4]_ Step 9: Modify the backup cron jobs. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since Cedar Backup should be run as root, one way to configure the cron job is to add a line like this to your ``/etc/crontab`` file: :: 30 00 * * * root cback3 all Or, you can create an executable script containing just these lines and place that file in the ``/etc/cron.daily`` directory: :: #/bin/sh cback3 all You should consider adding the ``--output`` or ``-O`` switch to your ``cback3`` command-line in cron. This will result in larger logs, but could help diagnose problems when commands like ``cdrecord`` or ``mkisofs`` fail mysteriously. |note| For general information about using cron, see the manpage for crontab(5). On a Debian system, execution of daily backups is controlled by the file ``/etc/cron.d/cedar-backup3``. As installed, this file contains several different settings, all commented out. Uncomment the “Single machine (pool of one)” entry in the file, and change the line so that the backup goes off when you want it to. .. _cedar-config-client: Setting up a Client Peer Node ----------------------------- Cedar Backup has been designed to backup entire “pools” of machines. In any given pool, there is one master and some number of clients. Most of the work takes place on the master, so configuring a client is a little simpler than configuring a master. Backups are designed to take place over an RSH or SSH connection. Because RSH is generally considered insecure, you are encouraged to use SSH rather than RSH. This document will only describe how to configure Cedar Backup to use SSH; if you want to use RSH, you're on your own. Once you complete all of these configuration steps, your backups will run as scheduled out of cron. Any errors that occur will be reported in daily emails to your root user (or the user that receives root's email). If you don't receive any emails, then you know your backup worked. *Note:* all of these configuration steps should be run as the root user, unless otherwise indicated. |note| See :doc:`securingssh` for some important notes on how to optionally further secure password-less SSH connections to your clients. Step 1: Decide when you will run your backup. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are four parts to a Cedar Backup run: collect, stage, store and purge. The usual way of setting off these steps is through a set of cron jobs. Although you won't create your cron jobs just yet, you should decide now when you will run your backup so you are prepared for later. Backing up large directories and creating ISO filesystem images can be intensive operations, and could slow your computer down significantly. Choose a backup time that will not interfere with normal use of your computer. Usually, you will want the backup to occur every day, but it is possible to configure cron to execute the backup only one day per week, three days per week, etc. |warning| Because of the way Cedar Backup works, you must ensure that your backup *always* runs on the first day of your configured week. This is because Cedar Backup will only clear incremental backup information and re-initialize your media when running on the first day of the week. If you skip running Cedar Backup on the first day of the week, your backups will likely be “confused” until the next week begins, or until you re-run the backup using the ``--full`` flag. Step 2: Make sure email works. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cedar Backup relies on email for problem notification. This notification works through the magic of cron. Cron will email any output from each job it executes to the user associated with the job. Since by default Cedar Backup only writes output to the terminal if errors occur, this neatly ensures that notification emails will only be sent out if errors occur. In order to receive problem notifications, you must make sure that email works for the user which is running the Cedar Backup cron jobs (typically root). Refer to your distribution's documentation for information on how to configure email on your system. Note that you may prefer to configure root's email to forward to some other user, so you do not need to check the root user's mail in order to see Cedar Backup errors. Step 3: Configure the master in your backup pool. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You will not be able to complete the client configuration until at least step 3 of the master's configuration has been completed. In particular, you will need to know the master's public SSH identity to fully configure a client. To find the master's public SSH identity, log in as the backup user on the master and ``cat`` the public identity file ``~/.ssh/id_rsa.pub``: :: user@machine> cat ~/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEA0vOKjlfwohPg1oPRdrmwHk75l3mI9Tb/WRZfVnu2Pw69 uyphM9wBLRo6QfOC2T8vZCB8o/ZIgtAM3tkM0UgQHxKBXAZ+H36TOgg7BcI20I93iGtzpsMA/uXQy8kH HgZooYqQ9pw+ZduXgmPcAAv2b5eTm07wRqFt/U84k6bhTzs= user@machine Step 4: Configure your backup user. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Choose a user to be used for backups. Some platforms may come with a "ready made" backup user. For other platforms, you may have to create a user yourself. You may choose any id you like, but a descriptive name such as ``backup`` or ``cback`` is a good choice. See your distribution's documentation for information on how to add a user. |note| Standard Debian systems come with a user named ``backup``. You may choose to stay with this user or create another one. Once you have created your backup user, you must create an SSH keypair for it. Log in as your backup user, and then run the command ``ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa``: :: user@machine> ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa Generating public/private rsa key pair. Created directory '/home/user/.ssh'. Your identification has been saved in /home/user/.ssh/id_rsa. Your public key has been saved in /home/user/.ssh/id_rsa.pub. The key fingerprint is: 11:3e:ad:72:95:fe:96:dc:1e:3b:f4:cc:2c:ff:15:9e user@machine The default permissions for this directory should be fine. However, if the directory existed before you ran ``ssh-keygen``, then you may need to modify the permissions. Make sure that the ``~/.ssh`` directory is readable only by the backup user (i.e. mode ``700``), that the ``~/.ssh/id_rsa`` file is only readable and writable only by the backup user (i.e. mode ``600``) and that the ``~/.ssh/id_rsa.pub`` file is writable only by the backup user (i.e. mode ``600`` or mode ``644``). Finally, take the master's public SSH identity (which you found in step 2) and cut-and-paste it into the file ``~/.ssh/authorized_keys``. Make sure the identity value is pasted into the file *all on one line*, and that the ``authorized_keys`` file is owned by your backup user and has permissions ``600``. If you have other preferences or standard ways of setting up your users' SSH configuration (i.e. different key type, etc.), feel free to do things your way. The important part is that the master must be able to SSH into a client *with no password entry required*. Step 5: Create your backup tree. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cedar Backup requires a backup directory tree on disk. This directory tree must be roughly as big as the amount of data that will be backed up on a nightly basis (more if you elect not to purge it all every night). You should create a collect directory and a working (temporary) directory. One recommended layout is this: :: /opt/ backup/ collect/ tmp/ If you will be backing up sensitive information (i.e. password files), it is recommended that these directories be owned by the backup user (whatever you named it), with permissions ``700``. |note| You don't have to use ``/opt`` as the root of your directory structure. Use anything you would like. I use ``/opt`` because it is my “dumping ground” for filesystems that Debian does not manage. Some users have requested that the Debian packages set up a more "standard" location for backups right out-of-the-box. I have resisted doing this because it's difficult to choose an appropriate backup location from within the package. If you would prefer, you can create the backup directory structure within some existing Debian directory such as ``/var/backups`` or ``/var/tmp``. Step 6: Create the Cedar Backup configuration file. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Following the instructions in *Configuration File Format* (above), create a configuration file for your machine. Since you are working with a client, you must configure all action-specific sections for the collect and purge actions. The usual location for the Cedar Backup config file is ``/etc/cback3.conf``. If you change the location, make sure you edit your cronjobs (below) to point the ``cback3`` script at the correct config file (using the ``--config`` option). |warning| Configuration files should always be writable only by root (or by the file owner, if the owner is not root). If you intend to place confidental information into the Cedar Backup configuration file, make sure that you set the filesystem permissions on the file appropriately. For instance, if you configure any extensions that require passwords or other similar information, you should make the file readable only to root or to the file owner (if the owner is not root). Step 7: Validate the Cedar Backup configuration file. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the command ``cback3 validate`` to validate your configuration file. This command checks that the configuration file can be found and parsed, and also checks for typical configuration problems. This command *only* validates configuration on the one client, not the master or any other clients in a pool. *Note:* the most common cause of configuration problems is in not closing XML tags properly. Any XML tag that is “opened” must be “closed” appropriately. Step 8: Test your backup. ~~~~~~~~~~~~~~~~~~~~~~~~~ Use the command ``cback3 --full collect purge``. If the command completes with no output, then the backup was run successfully. Just to be sure that everything worked properly, check the logfile (``/var/log/cback3.log``) for errors. Step 9: Modify the backup cron jobs. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since Cedar Backup should be run as root, you should add a set of lines like this to your ``/etc/crontab`` file: :: 30 00 * * * root cback3 collect 30 06 * * * root cback3 purge You should consider adding the ``--output`` or ``-O`` switch to your ``cback3`` command-line in cron. This will result in larger logs, but could help diagnose problems when commands like ``cdrecord`` or ``mkisofs`` fail mysteriously. You will need to coordinate the collect and purge actions on the client so that the collect action completes before the master attempts to stage, and so that the purge action does not begin until after the master has completed staging. Usually, allowing an hour or two between steps should be sufficient. [5]_ |note| For general information about using cron, see the manpage for crontab(5). On a Debian system, execution of daily backups is controlled by the file ``/etc/cron.d/cedar-backup3``. As installed, this file contains several different settings, all commented out. Uncomment the “Client machine” entries in the file, and change the lines so that the backup goes off when you want it to. .. _cedar-config-master: Setting up a Master Peer Node ----------------------------- Cedar Backup has been designed to backup entire “pools” of machines. In any given pool, there is one master and some number of clients. Most of the work takes place on the master, so configuring a master is somewhat more complicated than configuring a client. Backups are designed to take place over an RSH or SSH connection. Because RSH is generally considered insecure, you are encouraged to use SSH rather than RSH. This document will only describe how to configure Cedar Backup to use SSH; if you want to use RSH, you're on your own. Once you complete all of these configuration steps, your backups will run as scheduled out of cron. Any errors that occur will be reported in daily emails to your root user (or whichever other user receives root's email). If you don't receive any emails, then you know your backup worked. *Note:* all of these configuration steps should be run as the root user, unless otherwise indicated. |tip| This setup procedure discusses how to set up Cedar Backup in the “normal case” for a master. If you would like to modify the way Cedar Backup works (for instance, by ignoring the store stage and just letting your backup sit in a staging directory), you can do that. You'll just have to modify the procedure below based on information in the remainder of the manual. Step 1: Decide when you will run your backup. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are four parts to a Cedar Backup run: collect, stage, store and purge. The usual way of setting off these steps is through a set of cron jobs. Although you won't create your cron jobs just yet, you should decide now when you will run your backup so you are prepared for later. Keep in mind that you do not necessarily have to run the collect action on the master. See notes further below for more information. Backing up large directories and creating ISO filesystem images can be intensive operations, and could slow your computer down significantly. Choose a backup time that will not interfere with normal use of your computer. Usually, you will want the backup to occur every day, but it is possible to configure cron to execute the backup only one day per week, three days per week, etc. |warning| Because of the way Cedar Backup works, you must ensure that your backup *always* runs on the first day of your configured week. This is because Cedar Backup will only clear incremental backup information and re-initialize your media when running on the first day of the week. If you skip running Cedar Backup on the first day of the week, your backups will likely be “confused” until the next week begins, or until you re-run the backup using the ``--full`` flag. Step 2: Make sure email works. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cedar Backup relies on email for problem notification. This notification works through the magic of cron. Cron will email any output from each job it executes to the user associated with the job. Since by default Cedar Backup only writes output to the terminal if errors occur, this neatly ensures that notification emails will only be sent out if errors occur. In order to receive problem notifications, you must make sure that email works for the user which is running the Cedar Backup cron jobs (typically root). Refer to your distribution's documentation for information on how to configure email on your system. Note that you may prefer to configure root's email to forward to some other user, so you do not need to check the root user's mail in order to see Cedar Backup errors. Step 3: Configure your writer device. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before using Cedar Backup, your writer device must be properly configured. If you have configured your CD/DVD writer hardware to work through the normal filesystem device path, then you just need to know the path to the device on disk (something like ``/dev/cdrw``). Cedar Backup will use the this device path both when talking to a command like ``cdrecord`` and when doing filesystem operations like running media validation. Your other option is to configure your CD writer hardware like a SCSI device (either because it *is* a SCSI device or because you are using some sort of interface that makes it look like one). In this case, Cedar Backup will use the SCSI id when talking to ``cdrecord`` and the device path when running filesystem operations. See *Configuring your Writer Device* for more information on writer devices and how they are configured. |note| There is no need to set up your CD/DVD device if you have decided not to execute the store action. Due to the underlying utilities that Cedar Backup uses, the SCSI id may only be used for CD writers, *not* DVD writers. Step 4: Configure your backup user. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Choose a user to be used for backups. Some platforms may come with a “ready made” backup user. For other platforms, you may have to create a user yourself. You may choose any id you like, but a descriptive name such as ``backup`` or ``cback`` is a good choice. See your distribution's documentation for information on how to add a user. |note| Standard Debian systems come with a user named ``backup``. You may choose to stay with this user or create another one. Once you have created your backup user, you must create an SSH keypair for it. Log in as your backup user, and then run the command ``ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa``: :: user@machine> ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa Generating public/private rsa key pair. Created directory '/home/user/.ssh'. Your identification has been saved in /home/user/.ssh/id_rsa. Your public key has been saved in /home/user/.ssh/id_rsa.pub. The key fingerprint is: 11:3e:ad:72:95:fe:96:dc:1e:3b:f4:cc:2c:ff:15:9e user@machine The default permissions for this directory should be fine. However, if the directory existed before you ran ``ssh-keygen``, then you may need to modify the permissions. Make sure that the ``~/.ssh`` directory is readable only by the backup user (i.e. mode ``700``), that the ``~/.ssh/id_rsa`` file is only readable and writable by the backup user (i.e. mode ``600``) and that the ``~/.ssh/id_rsa.pub`` file is writable only by the backup user (i.e. mode ``600`` or mode ``644``). If you have other preferences or standard ways of setting up your users' SSH configuration (i.e. different key type, etc.), feel free to do things your way. The important part is that the master must be able to SSH into a client *with no password entry required*. Step 5: Create your backup tree. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cedar Backup requires a backup directory tree on disk. This directory tree must be roughly large enough hold twice as much data as will be backed up from the entire pool on a given night, plus space for whatever is collected on the master itself. This will allow for all three operations - collect, stage and store - to have enough space to complete. Note that if you elect not to purge the staging directory every night, you will need even more space. You should create a collect directory, a staging directory and a working (temporary) directory. One recommended layout is this: :: /opt/ backup/ collect/ stage/ tmp/ If you will be backing up sensitive information (i.e. password files), it is recommended that these directories be owned by the backup user (whatever you named it), with permissions ``700``. |note| You don't have to use ``/opt`` as the root of your directory structure. Use anything you would like. I use ``/opt`` because it is my “dumping ground” for filesystems that Debian does not manage. Some users have requested that the Debian packages set up a more “standard” location for backups right out-of-the-box. I have resisted doing this because it's difficult to choose an appropriate backup location from within the package. If you would prefer, you can create the backup directory structure within some existing Debian directory such as ``/var/backups`` or ``/var/tmp``. Step 6: Create the Cedar Backup configuration file. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Following the instructions in *Configuration File Foramt** (above), create a configuration file for your machine. Since you are working with a master machine, you would typically configure all four action-specific sections: collect, stage, store and purge. |note| Note that the master can treat itself as a “client” peer for certain actions. As an example, if you run the collect action on the master, then you will stage that data by configuring a local peer representing the master. Something else to keep in mind is that you do not really have to run the collect action on the master. For instance, you may prefer to just use your master machine as a “consolidation point” machine that just collects data from the other client machines in a backup pool. In that case, there is no need to collect data on the master itself. The usual location for the Cedar Backup config file is ``/etc/cback3.conf``. If you change the location, make sure you edit your cronjobs (below) to point the ``cback3`` script at the correct config file (using the ``--config`` option). |warning| Configuration files should always be writable only by root (or by the file owner, if the owner is not root). If you intend to place confidental information into the Cedar Backup configuration file, make sure that you set the filesystem permissions on the file appropriately. For instance, if you configure any extensions that require passwords or other similar information, you should make the file readable only to root or to the file owner (if the owner is not root). Step 7: Validate the Cedar Backup configuration file. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Use the command ``cback3 validate`` to validate your configuration file. This command checks that the configuration file can be found and parsed, and also checks for typical configuration problems, such as invalid CD/DVD device entries. This command *only* validates configuration on the master, not any clients that the master might be configured to connect to. *Note:* the most common cause of configuration problems is in not closing XML tags properly. Any XML tag that is “opened” must be “closed” appropriately. Step 8: Test connectivity to client machines. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This step must wait until after your client machines have been at least partially configured. Once the backup user(s) have been configured on the client machine(s) in a pool, attempt an SSH connection to each client. Log in as the backup user on the master, and then use the command ``ssh user@machine`` where user is the name of backup user *on the client machine*, and machine is the name of the client machine. If you are able to log in successfully to each client without entering a password, then things have been configured properly. Otherwise, double-check that you followed the user setup instructions for the master and the clients. Step 9: Test your backup. ~~~~~~~~~~~~~~~~~~~~~~~~~ Make sure that you have configured all of the clients in your backup pool. On all of the clients, execute ``cback3 --full collect``. (You will probably have already tested this command on each of the clients, so it should succeed.) When all of the client backups have completed, place a valid CD/DVD disc in your drive, and then use the command ``cback3 --full all``. You should execute this command as root. If the command completes with no output, then the backup was run successfully. Just to be sure that everything worked properly, check the logfile (``/var/log/cback3.log``) on the master and each of the clients, and also mount the CD/DVD disc on the master to be sure it can be read. You may also want to run ``cback3 purge`` on the master and each client once you have finished validating that everything worked. *If Cedar Backup ever completes “normally” but the disc that is created is not usable, please report this as a bug. To be safe, always enable the consistency check option in the store configuration section.* Step 10: Modify the backup cron jobs. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Since Cedar Backup should be run as root, you should add a set of lines like this to your ``/etc/crontab`` file: :: 30 00 * * * root cback3 collect 30 02 * * * root cback3 stage 30 04 * * * root cback3 store 30 06 * * * root cback3 purge You should consider adding the ``--output`` or ``-O`` switch to your ``cback3`` command-line in cron. This will result in larger logs, but could help diagnose problems when commands like ``cdrecord`` or ``mkisofs`` fail mysteriously. You will need to coordinate the collect and purge actions on clients so that their collect actions complete before the master attempts to stage, and so that their purge actions do not begin until after the master has completed staging. Usually, allowing an hour or two between steps should be sufficient. |note| For general information about using cron, see the manpage for crontab(5). On a Debian system, execution of daily backups is controlled by the file ``/etc/cron.d/cedar-backup3``. As installed, this file contains several different settings, all commented out. Uncomment the “Master machine” entries in the file, and change the lines so that the backup goes off when you want it to. .. _cedar-config-writer: Configuring your Writer Device ------------------------------ Device Types ~~~~~~~~~~~~ In order to execute the store action, you need to know how to identify your writer device. Cedar Backup supports two kinds of device types: CD writers and DVD writers. DVD writers are always referenced through a filesystem device name (i.e. ``/dev/dvd``). CD writers can be referenced either through a SCSI id, or through a filesystem device name. Which you use depends on your operating system and hardware. Devices identified by by device name ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For all DVD writers, and for CD writers on certain platforms, you will configure your writer device using only a device name. If your writer device works this way, you should just specify in configuration. You can either leave blank or remove it completely. The writer device will be used both to write to the device and for filesystem operations --- for instance, when the media needs to be mounted to run the consistency check. Devices identified by SCSI id ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Cedar Backup can use devices identified by SCSI id only when configured to use the ``cdwriter`` device type. In order to use a SCSI device with Cedar Backup, you must know both the SCSI id and the device name . The SCSI id will be used to write to media using ``cdrecord``; and the device name will be used for other filesystem operations. A true SCSI device will always have an address ``scsibus,target,lun`` (i.e. ``1,6,2``). This should hold true on most UNIX-like systems including Linux and the various BSDs (although I do not have a BSD system to test with currently). The SCSI address represents the location of your writer device on the one or more SCSI buses that you have available on your system. On some platforms, it is possible to reference non-SCSI writer devices (i.e. an IDE CD writer) using an emulated SCSI id. If you have configured your non-SCSI writer device to have an emulated SCSI id, provide the filesystem device path in and the SCSI id in , just like for a real SCSI device. You should note that in some cases, an emulated SCSI id takes the same form as a normal SCSI id, while in other cases you might see a method name prepended to the normal SCSI id (i.e. “ATA:1,1,1”). Linux Notes ~~~~~~~~~~~ On a Linux system, IDE writer devices often have a emulated SCSI address, which allows SCSI-based software to access the device through an IDE-to-SCSI interface. Under these circumstances, the first IDE writer device typically has an address ``0,0,0``. However, support for the IDE-to-SCSI interface has been deprecated and is not well-supported in newer kernels (kernel 2.6.x and later). Newer Linux kernels can address ATA or ATAPI drives without SCSI emulation by prepending a “method” indicator to the emulated device address. For instance, ``ATA:0,0,0`` or ``ATAPI:0,0,0`` are typical values. However, even this interface is deprecated as of late 2006, so with relatively new kernels you may be better off using the filesystem device path directly rather than relying on any SCSI emulation. Here are some hints about how to find your Linux CD writer hardware. First, try to reference your device using the filesystem device path: :: cdrecord -prcap dev=/dev/cdrom Running this command on my hardware gives output that looks like this (just the top few lines): :: Device type : Removable CD-ROM Version : 0 Response Format: 2 Capabilities : Vendor_info : 'LITE-ON ' Identification : 'DVDRW SOHW-1673S' Revision : 'JS02' Device seems to be: Generic mmc2 DVD-R/DVD-RW. Drive capabilities, per MMC-3 page 2A: If this works, and the identifying information at the top of the output looks like your CD writer device, you've probably found a working configuration. Place the device path into and leave blank. If this doesn't work, you should try to find an ATA or ATAPI device: :: cdrecord -scanbus dev=ATA cdrecord -scanbus dev=ATAPI On my development system, I get a result that looks something like this for ATA: :: scsibus1: 1,0,0 100) 'LITE-ON ' 'DVDRW SOHW-1673S' 'JS02' Removable CD-ROM 1,1,0 101) * 1,2,0 102) * 1,3,0 103) * 1,4,0 104) * 1,5,0 105) * 1,6,0 106) * 1,7,0 107) * Again, if you get a result that you recognize, you have again probably found a working configuraton. Place the associated device path (in my case, ``/dev/cdrom``) into and put the emulated SCSI id (in this case, ``ATA:1,0,0``) into . Any further discussion of how to configure your CD writer hardware is outside the scope of this document. If you have tried the hints above and still can't get things working, you may want to reference the Linux CDROM HOWTO (``__) or the ATA RAID HOWTO (``__) for more information. Mac OS X Notes ~~~~~~~~~~~~~~ On a Mac OS X (darwin) system, things get strange. Apple has abandoned traditional SCSI device identifiers in favor of a system-wide resource id. So, on a Mac, your writer device will have a name something like ``IOCompactDiscServices`` (for a CD writer) or ``IODVDServices`` (for a DVD writer). If you have multiple drives, the second drive probably has a number appended, i.e. ``IODVDServices/2`` for the second DVD writer. You can try to figure out what the name of your device is by grepping through the output of the command ``ioreg -l``. [6]_ Unfortunately, even if you can figure out what device to use, I can't really support the store action on this platform. In OS X, the “automount” function of the Finder interferes significantly with Cedar Backup's ability to mount and unmount media and write to the CD or DVD hardware. The Cedar Backup writer and image functionality does work on this platform, but the effort required to fight the operating system about who owns the media and the device makes it nearly impossible to execute the store action successfully. .. _cedar-config-blanking: Optimized Blanking Stategy -------------------------- When the optimized blanking strategy has not been configured, Cedar Backup uses a simplistic approach: rewritable media is blanked at the beginning of every week, period. Since rewritable media can be blanked only a finite number of times before becoming unusable, some users --- especially users of rewritable DVD media with its large capacity --- may prefer to blank the media less often. If the optimized blanking strategy is configured, Cedar Backup will use a blanking factor and attempt to determine whether future backups will fit on the current media. If it looks like backups will fit, then the media will not be blanked. This feature will only be useful (assuming single disc is used for the whole week's backups) if the estimated total size of the weekly backup is considerably smaller than the capacity of the media (no more than 50% of the total media capacity), and only if the size of the backup can be expected to remain fairly constant over time (no frequent rapid growth expected). There are two blanking modes: daily and weekly. If the weekly blanking mode is set, Cedar Backup will only estimate future capacity (and potentially blank the disc) once per week, on the starting day of the week. If the daily blanking mode is set, Cedar Backup will estimate future capacity (and potentially blank the disc) every time it is run. *You should only use the daily blanking mode in conjunction with daily collect configuration, otherwise you will risk losing data.* If you are using the daily blanking mode, you can typically set the blanking value to 1.0. This will cause Cedar Backup to blank the media whenever there is not enough space to store the current day's backup. If you are using the weekly blanking mode, then finding the correct blanking factor will require some experimentation. Cedar Backup estimates future capacity based on the configured blanking factor. The disc will be blanked if the following relationship is true: :: bytes available / (1 + bytes required) LE blanking factor Another way to look at this is to consider the blanking factor as a sort of (upper) backup growth estimate: :: Total size of weekly backup / Full backup size at the start of the week This ratio can be estimated using a week or two of previous backups. For instance, take this example, where March 10 is the start of the week and March 4 through March 9 represent the incremental backups from the previous week: :: /opt/backup/staging# du -s 2007/03/* 3040 2007/03/01 3044 2007/03/02 6812 2007/03/03 3044 2007/03/04 3152 2007/03/05 3056 2007/03/06 3060 2007/03/07 3056 2007/03/08 4776 2007/03/09 6812 2007/03/10 11824 2007/03/11 In this case, the ratio is approximately 4: :: 6812 + (3044 + 3152 + 3056 + 3060 + 3056 + 4776) / 6812 = 3.9571 To be safe, you might choose to configure a factor of 5.0. Setting a higher value reduces the risk of exceeding media capacity mid-week but might result in blanking the media more often than is necessary. If you run out of space mid-week, then the solution is to run the rebuild action. If this happens frequently, a higher blanking factor value should be used. ---------- *Previous*: :doc:`commandline` • *Next*: :doc:`extensions` ---------- .. [1] See ``__ for a basic introduction to XML. .. [2] See :doc:`basic` .. [3] See ``__ .. [4] See ``__. .. [5] See :doc:`basic` .. [6] Thanks to the file README.macosX in the cdrtools-2.01+01a01 source tree for this information .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/copyright.rst0000644000000000000000000004157514567004737016137 0ustar00.. _cedar-copyright: Copyright ========= :: Copyright (c) 2004-2011,2013-2015,2020 Kenneth J. Pronovici This work is free; you can redistribute it and/or modify it under the terms of the GNU General Public License (the "GPL"), Version 2, as published by the Free Software Foundation. For the purposes of the GPL, the "preferred form of modification" for this work is the original reStructuredText files. If you choose to distribute this work in a compiled form (i.e. if you distribute HTML, PDF or Postscript documents based on the original restructuredText files), you must also consider image files to be "source code" if those images are required in order to construct a complete and readable compiled version of the work. This work 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. Copies of the GNU General Public License are available from the Free Software Foundation website, http://www.gnu.org/. You may also write the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ==================================================================== GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS ==================================================================== ---------- *Previous*: :doc:`securingssh` ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/depends.rst0000644000000000000000000002662714567004737015552 0ustar00.. _cedar-depends: Dependencies ============ Python 3.8 (or later) +---------------+------------------------------------------------------------------+ | Source | URL | +===============+==================================================================+ | upstream | ``__ | +---------------+------------------------------------------------------------------+ | Debian | ``__ | +---------------+------------------------------------------------------------------+ | RPM | ``__ | +---------------+------------------------------------------------------------------+ If you can't find a package for your system, install from the package source, using the “upstream” link. RSH Server and Client Although Cedar Backup will technically work with any RSH-compatible server and client pair (such as the classic “rsh” client), most users should only use an SSH (secure shell) server and client. The defacto standard today is OpenSSH. Some systems package the server and the client together, and others package the server and the client separately. Note that master nodes need an SSH client, and client nodes need to run an SSH server. +---------------+------------------------------------------------------------------+ | Source | URL | +===============+==================================================================+ | upstream | ``__ | +---------------+------------------------------------------------------------------+ | Debian | ``__ | +---------------+------------------------------------------------------------------+ | RPM | ``__ | +---------------+------------------------------------------------------------------+ If you can't find SSH client or server packages for your system, install from the package source, using the “upstream” link. ``mkisofs`` The ``mkisofs`` command is used create ISO filesystem images that can later be written to backup media. On Debian platforms, ``mkisofs`` is not distributed and ``genisoimage`` is used instead. The Debian package takes care of this for you. +---------------+-------------------------------------------------------------------+ | Source | URL | +===============+===================================================================+ | upstream | ``__ | +---------------+-------------------------------------------------------------------+ | RPM | ``__ | +---------------+-------------------------------------------------------------------+ If you can't find a package for your system, install from the package source, using the “upstream” link. ``cdrecord`` The ``cdrecord`` command is used to write ISO images to CD media in a backup device. On Debian platforms, ``cdrecord`` is not distributed and ``wodim`` is used instead. The Debian package takes care of this for you. +---------------+-------------------------------------------------------------------+ | Source | URL | +===============+===================================================================+ | upstream | ``__ | +---------------+-------------------------------------------------------------------+ | RPM | ``__ | +---------------+-------------------------------------------------------------------+ If you can't find a package for your system, install from the package source, using the “upstream” link. ``dvd+rw-tools`` The dvd+rw-tools package provides the ``growisofs`` utility, which is used to write ISO images to DVD media in a backup device. +---------------+-----------------------------------------------------------------------+ | Source | URL | +===============+=======================================================================+ | upstream | ``__ | +---------------+-----------------------------------------------------------------------+ | Debian | ``__ | +---------------+-----------------------------------------------------------------------+ | RPM | ``__ | +---------------+-----------------------------------------------------------------------+ If you can't find a package for your system, install from the package source, using the “upstream” link. ``eject`` and ``volname`` The ``eject`` command is used to open and close the tray on a backup device (if the backup device has a tray). Sometimes, the tray must be opened and closed in order to "reset" the device so it notices recent changes to a disc. The ``volname`` command is used to determine the volume name of media in a backup device. +---------------+-----------------------------------------------------------------+ | Source | URL | +===============+=================================================================+ | upstream | ``__ | +---------------+-----------------------------------------------------------------+ | Debian | ``__ | +---------------+-----------------------------------------------------------------+ | RPM | ``__ | +---------------+-----------------------------------------------------------------+ If you can't find a package for your system, install from the package source, using the “upstream” link. ``mount`` and ``umount`` The ``mount`` and ``umount`` commands are used to mount and unmount CD/DVD media after it has been written, in order to run a consistency check. +---------------+----------------------------------------------------------------+ | Source | URL | +===============+================================================================+ | upstream | ``__ | +---------------+----------------------------------------------------------------+ | Debian | ``__ | +---------------+----------------------------------------------------------------+ | RPM | ``__ | +---------------+----------------------------------------------------------------+ If you can't find a package for your system, install from the package source, using the “upstream” link. ``grepmail`` The ``grepmail`` command is used by the mbox extension to pull out only recent messages from mbox mail folders. +---------------+--------------------------------------------------------------------+ | Source | URL | +===============+====================================================================+ | upstream | ``__ | +---------------+--------------------------------------------------------------------+ | Debian | ``__ | +---------------+--------------------------------------------------------------------+ | RPM | ``__ | +---------------+--------------------------------------------------------------------+ If you can't find a package for your system, install from the package source, using the “upstream” link. ``gpg`` The ``gpg`` command is used by the encrypt extension to encrypt files. +---------------+-----------------------------------------------------------------+ | Source | URL | +===============+=================================================================+ | upstream | ``__ | +---------------+-----------------------------------------------------------------+ | Debian | ``__ | +---------------+-----------------------------------------------------------------+ | RPM | ``__ | +---------------+-----------------------------------------------------------------+ If you can't find a package for your system, install from the package source, using the “upstream” link. ``split`` The ``split`` command is used by the split extension to split up large files. This command is typically part of the core operating system install and is not distributed in a separate package. ``AWS CLI`` AWS CLI is Amazon's official command-line tool for interacting with the Amazon Web Services infrastruture. Cedar Backup uses AWS CLI to copy backup data up to Amazon S3 cloud storage. After you install AWS CLI, you need to configure your connection to AWS with an appropriate access id and access key. Amazon provides a good `setup guide `__. +---------------+-------------------------------------------------------+ | Source | URL | +===============+=======================================================+ | upstream | ``__ | +---------------+-------------------------------------------------------+ | Debian | ``__ | +---------------+-------------------------------------------------------+ The initial implementation of the amazons3 extension was written using AWS CLI 1.4. As of this writing, not all Linux distributions include a package for this version. On these platforms, the easiest way to install it is via PIP: ``apt-get install python3-pip``, and then ``pip3 install awscli``. The Debian package includes an appropriate dependency starting with the jessie release. ---------- *Previous*: :doc:`extenspec` • *Next*: :doc:`recovering` .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/extensions.rst0000644000000000000000000012512414567004737016317 0ustar00.. _cedar-extensions: Official Extensions =================== .. _cedar-extensions-sysinfo: System Information Extension ---------------------------- The System Information Extension is a simple Cedar Backup extension used to save off important system recovery information that might be useful when reconstructing a “broken” system. It is intended to be run either immediately before or immediately after the standard collect action. This extension saves off the following information to the configured Cedar Backup collect directory. Saved off data is always compressed using ``bzip2``. - Currently-installed Debian packages via ``dpkg --get-selections`` - Disk partition information via ``fdisk -l`` - System-wide mounted filesystem contents, via ``ls -laR`` The Debian-specific information is only collected on systems where ``/usr/bin/dpkg`` exists. To enable this extension, add the following section to the Cedar Backup configuration file: :: sysinfo CedarBackup3.extend.sysinfo executeAction 99 This extension relies on the options and collect configuration sections in the standard Cedar Backup configuration file, but requires no new configuration of its own. .. _cedar-extensions-amazons3: Amazon S3 Extension ------------------- The Amazon S3 extension writes data to Amazon S3 cloud storage rather than to physical media. It is intended to replace the store action, but you can also use it alongside the store action if you'd prefer to backup your data in more than one place. This extension must be run after the stage action. The underlying functionality relies on the `AWS CLI `__ toolset. Before you use this extension, you need to set up your Amazon S3 account and configure AWS CLI as detailed in Amazon's `setup guide `__. The extension assumes that the backup is being executed as root, and switches over to the configured backup user to run the ``aws`` program. So, make sure you configure the AWS CLI tools as the backup user and not root. (This is different than the ``cback-amazons3-sync`` tool, which executes AWS CLI command as the same user that is running the tool.) You can use whichever Amazon-supported authentication mechanism you would like when setting up connectivity for the AWS CLI. It's best to set up a separate user in the `IAM Console `__ rather than using your main administrative user. You probably want to lock down this user so that it can only take backup related actions in the AWS infrastructure. One option is to apply the ``AmazonS3FullAccess`` policy, which grants full access to the S3 infrastructure. If you would like to lock down the user even further, this appears to be the minimum set of permissions required for Cedar Backup, written as a JSON policy statement: :: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListBucket", "s3:ListObjects", "s3:PutObject", "s3:PutObjectAcl", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*" ] } ] } In the ``Resource`` section, be sure to list the name of your S3 bucket instead of ``my-bucket``. When using physical media via the standard store action, there is an implicit limit to the size of a backup, since a backup must fit on a single disc. Since there is no physical media, no such limit exists for Amazon S3 backups. This leaves open the possibility that Cedar Backup might construct an unexpectedly-large backup that the administrator is not aware of. Over time, this might become expensive, either in terms of network bandwidth or in terms of Amazon S3 storage and I/O charges. To mitigate this risk, set a reasonable maximum size using the configuration elements shown below. If the backup fails, you have a chance to review what made the backup larger than you expected, and you can either correct the problem (i.e. remove a large temporary directory that got inadvertently included in the backup) or change configuration to take into account the new "normal" maximum size. You can optionally configure Cedar Backup to encrypt data before sending it to S3. To do that, provide a complete command line using the ``${input}`` and ``${output}`` variables to represent the original input file and the encrypted output file. This command will be executed as the backup user. For instance, you can use something like this with GPG: :: /usr/bin/gpg -c --no-use-agent --batch --yes --passphrase-file /home/backup/.passphrase -o ${output} ${input} The GPG mechanism depends on a strong passphrase for security. One way to generate a strong passphrase is using your system random number generator, i.e.: :: dd if=/dev/urandom count=20 bs=1 | xxd -ps (See `StackExchange `__ for more details about that advice.) If you decide to use encryption, make sure you save off the passphrase in a safe place, so you can get at your backup data later if you need to. And obviously, make sure to set permissions on the passphrase file so it can only be read by the backup user. To enable this extension, add the following section to the Cedar Backup configuration file: :: amazons3 CedarBackup3.extend.amazons3 executeAction 201 This extension relies on the options and staging configuration sections in the standard Cedar Backup configuration file, and then also requires its own ``amazons3`` configuration section. This is an example configuration section with encryption disabled: :: example.com-backup/staging The following elements are part of the Amazon S3 configuration section: ``warn_midnite`` Whether to generate warnings for crossing midnite. This field indicates whether warnings should be generated if the Amazon S3 operation has to cross a midnite boundary in order to find data to write to the cloud. For instance, a warning would be generated if valid data was only found in the day before or day after the current day. Configuration for some users is such that the amazons3 operation will always cross a midnite boundary, so they will not care about this warning. Other users will expect to never cross a boundary, and want to be notified that something “strange” might have happened. This field is optional. If it doesn't exist, then ``N`` will be assumed. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``s3_bucket`` The name of the Amazon S3 bucket that data will be written to. This field configures the S3 bucket that your data will be written to. In S3, buckets are named globally. For uniqueness, you would typically use the name of your domain followed by some suffix, such as ``example.com-backup``. If you want, you can specify a subdirectory within the bucket, such as ``example.com-backup/staging``. *Restrictions:* Must be non-empty. ``encrypt`` Command used to encrypt backup data before upload to S3 If this field is provided, then data will be encrypted before it is uploaded to Amazon S3. You must provide the entire command used to encrypt a file, including the ``${input}`` and ``${output}`` variables. An example GPG command is shown above, but you can use any mechanism you choose. The command will be run as the configured backup user. *Restrictions:* If provided, must be non-empty. ``full_size_limit`` Maximum size of a full backup If this field is provided, then a size limit will be applied to full backups. If the total size of the selected staging directory is greater than the limit, then the backup will fail. You can enter this value in two different forms. It can either be a simple number, in which case the value is assumed to be in bytes; or it can be a number followed by a unit (KB, MB, GB). Valid examples are “10240”, “250 MB” or “1.1 GB”. *Restrictions:* Must be a value as described above, greater than zero. ``incr_size_limit`` Maximum size of an incremental backup If this field is provided, then a size limit will be applied to incremental backups. If the total size of the selected staging directory is greater than the limit, then the backup will fail. You can enter this value in two different forms. It can either be a simple number, in which case the value is assumed to be in bytes; or it can be a number followed by a unit (KB, MB, GB). Valid examples are “10240”, “250 MB” or “1.1 GB”. *Restrictions:* Must be a value as described above, greater than zero. .. _cedar-extensions-subversion: Subversion Extension -------------------- The Subversion Extension is a Cedar Backup extension used to back up `Subversion `__ version control repositories via the Cedar Backup command line. It is intended to be run either immediately before or immediately after the standard collect action. Each configured Subversion repository can be backed using the same collect modes allowed for filesystems in the standard Cedar Backup collect action (weekly, daily, incremental) and the output can be compressed using either ``gzip`` or ``bzip2``. There are two different kinds of Subversion repositories at this writing: BDB (Berkeley Database) and FSFS (a "filesystem within a filesystem"). This extension backs up both kinds of repositories in the same way, using ``svnadmin dump`` in an incremental mode. It turns out that FSFS repositories can also be backed up just like any other filesystem directory. If you would rather do the backup that way, then use the normal collect action rather than this extension. If you decide to do that, be sure to consult the Subversion documentation and make sure you understand the limitations of this kind of backup. To enable this extension, add the following section to the Cedar Backup configuration file: :: subversion CedarBackup3.extend.subversion executeAction 99 This extension relies on the options and collect configuration sections in the standard Cedar Backup configuration file, and then also requires its own ``subversion`` configuration section. This is an example Subversion configuration section: :: incr bzip2 /opt/public/svn/docs /opt/public/svn/web gzip /opt/private/svn daily The following elements are part of the Subversion configuration section: ``collect_mode`` Default collect mode. The collect mode describes how frequently a Subversion repository is backed up. The Subversion extension recognizes the same collect modes as the standard Cedar Backup collect action (see :doc:`basic`). This value is the collect mode that will be used by default during the backup process. Individual repositories (below) may override this value. If *all* individual repositories provide their own value, then this default value may be omitted from configuration. *Note:* if your backup device does not suppport multisession discs, then you should probably use the ``daily`` collect mode to avoid losing data. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``compress_mode`` Default compress mode. Subversion repositories backups are just specially-formatted text files, and often compress quite well using ``gzip`` or ``bzip2``. The compress mode describes how the backed-up data will be compressed, if at all. This value is the compress mode that will be used by default during the backup process. Individual repositories (below) may override this value. If *all* individual repositories provide their own value, then this default value may be omitted from configuration. *Restrictions:* Must be one of ``none``, ``gzip`` or ``bzip2``. ``repository`` A Subversion repository be collected. This is a subsection which contains information about a specific Subversion repository to be backed up. This section can be repeated as many times as is necessary. At least one repository or repository directory must be configured. The ``repository`` subsection contains the following fields: ``collect_mode`` Collect mode for this repository. This field is optional. If it doesn't exist, the backup will use the default collect mode. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``compress_mode`` Compress mode for this repository. This field is optional. If it doesn't exist, the backup will use the default compress mode. *Restrictions:* Must be one of ``none``, ``gzip`` or ``bzip2``. ``abs_path`` Absolute path of the Subversion repository to back up. *Restrictions:* Must be an absolute path. ``repository_dir`` A Subversion parent repository directory be collected. This is a subsection which contains information about a Subversion parent repository directory to be backed up. Any subdirectory immediately within this directory is assumed to be a Subversion repository, and will be backed up. This section can be repeated as many times as is necessary. At least one repository or repository directory must be configured. The ``repository_dir`` subsection contains the following fields: ``collect_mode`` Collect mode for this repository. This field is optional. If it doesn't exist, the backup will use the default collect mode. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``compress_mode`` Compress mode for this repository. This field is optional. If it doesn't exist, the backup will use the default compress mode. *Restrictions:* Must be one of ``none``, ``gzip`` or ``bzip2``. ``abs_path`` Absolute path of the Subversion repository to back up. *Restrictions:* Must be an absolute path. ``exclude`` List of paths or patterns to exclude from the backup. This is a subsection which contains a set of paths and patterns to be excluded within this subversion parent directory. This section is entirely optional, and if it exists can also be empty. The exclude subsection can contain one or more of each of the following fields: ``rel_path`` A relative path to be excluded from the backup. The path is assumed to be relative to the subversion parent directory itself. For instance, if the configured subversion parent directory is ``/opt/svn`` a configured relative path of ``software`` would exclude the path ``/opt/svn/software``. This field can be repeated as many times as is necessary. *Restrictions:* Must be non-empty. ``pattern`` A pattern to be excluded from the backup. The pattern must be a Python regular expression. It is assumed to be bounded at front and back by the beginning and end of the string (i.e. it is treated as if it begins with ``^`` and ends with ``$``). This field can be repeated as many times as is necessary. *Restrictions:* Must be non-empty .. _cedar-extensions-mysql: MySQL Extension --------------- The MySQL Extension is a Cedar Backup extension used to back up `MySQL `__ databases via the Cedar Backup command line. It is intended to be run either immediately before or immediately after the standard collect action. |note| This extension always produces a full backup. There is currently no facility for making incremental backups. If/when someone has a need for this and can describe how to do it, I will update this extension or provide another. The backup is done via the ``mysqldump`` command included with the MySQL product. Output can be compressed using ``gzip`` or ``bzip2``. Administrators can configure the extension either to back up all databases or to back up only specific databases. The extension assumes that all configured databases can be backed up by a single user. Often, the “root” database user will be used. An alternative is to create a separate MySQL “backup” user and grant that user rights to read (but not write) various databases as needed. This second option is probably your best choice. |warning| The extension accepts a username and password in configuration. However, you probably do not want to list those values in Cedar Backup configuration. This is because Cedar Backup will provide these values to ``mysqldump`` via the command-line ``--user`` and ``--password`` switches, which will be visible to other users in the process listing. Instead, you should configure the username and password in one of MySQL's configuration files. Typically, that would be done by putting a stanza like this in ``/root/.my.cnf``: :: [mysqldump] user = root password = Of course, if you are executing the backup as a user other than root, then you would create the file in that user's home directory instead. As a side note, it is also possible to configure ``.my.cnf`` such that Cedar Backup can back up a remote database server: :: [mysqldump] host = remote.host For this to work, you will also need to grant privileges properly for the user which is executing the backup. See your MySQL documentation for more information about how this can be done. Regardless of whether you are using ``~/.my.cnf`` or ``/etc/cback3.conf`` to store database login and password information, you should be careful about who is allowed to view that information. Typically, this means locking down permissions so that only the file owner can read the file contents (i.e. use mode ``0600``). To enable this extension, add the following section to the Cedar Backup configuration file: :: mysql CedarBackup3.extend.mysql executeAction 99 This extension relies on the options and collect configuration sections in the standard Cedar Backup configuration file, and then also requires its own ``mysql`` configuration section. This is an example MySQL configuration section: :: bzip2 Y If you have decided to configure login information in Cedar Backup rather than using MySQL configuration, then you would add the username and password fields to configuration: :: root password bzip2 Y The following elements are part of the MySQL configuration section: ``user`` Database user. The database user that the backup should be executed as. Even if you list more than one database (below) all backups must be done as the same user. Typically, this would be ``root`` (i.e. the database root user, not the system root user). This value is optional. You should probably configure the username and password in MySQL configuration instead, as discussed above. *Restrictions:* If provided, must be non-empty. ``password`` Password associated with the database user. This value is optional. You should probably configure the username and password in MySQL configuration instead, as discussed above. *Restrictions:* If provided, must be non-empty. ``compress_mode`` Compress mode. MySQL databases dumps are just specially-formatted text files, and often compress quite well using ``gzip`` or ``bzip2``. The compress mode describes how the backed-up data will be compressed, if at all. *Restrictions:* Must be one of ``none``, ``gzip`` or ``bzip2``. ``all`` Indicates whether to back up all databases. If this value is ``Y``, then all MySQL databases will be backed up. If this value is ``N``, then one or more specific databases must be specified (see below). If you choose this option, the entire database backup will go into one big dump file. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``database`` Named database to be backed up. If you choose to specify individual databases rather than all databases, then each database will be backed up into its own dump file. This field can be repeated as many times as is necessary. At least one database must be configured if the all option (above) is set to ``N``. You may not configure any individual databases if the all option is set to ``Y``. *Restrictions:* Must be non-empty. .. _cedar-extensions-postgresql: PostgreSQL Extension -------------------- This is a community-contributed extension provided by Antoine Beaupre ("The Anarcat"). I have added regression tests around the configuration parsing code and I will maintain this section in the user manual based on his source code documentation. The PostgreSQL Extension is a Cedar Backup extension used to back up `PostgreSQL `__ databases via the Cedar Backup command line. It is intended to be run either immediately before or immediately after the standard collect action. The backup is done via the ``pg_dump`` or ``pg_dumpall`` commands included with the PostgreSQL product. Output can be compressed using ``gzip`` or ``bzip2``. Administrators can configure the extension either to back up all databases or to back up only specific databases. The extension assumes that the current user has passwordless access to the database since there is no easy way to pass a password to the ``pg_dump`` client. This can be accomplished using appropriate configuration in the ``pg_hda.conf`` file. This extension always produces a full backup. There is currently no facility for making incremental backups. |warning| Once you place PostgreSQL configuration into the Cedar Backup configuration file, you should be careful about who is allowed to see that information. This is because PostgreSQL configuration will contain information about available PostgreSQL databases and usernames. Typically, you might want to lock down permissions so that only the file owner can read the file contents (i.e. use mode ``0600``). To enable this extension, add the following section to the Cedar Backup configuration file: :: postgresql CedarBackup3.extend.postgresql executeAction 99 This extension relies on the options and collect configuration sections in the standard Cedar Backup configuration file, and then also requires its own ``postgresql`` configuration section. This is an example PostgreSQL configuration section: :: bzip2 username Y If you decide to back up specific databases, then you would list them individually, like this: :: bzip2 username N db1 db2 The following elements are part of the PostgreSQL configuration section: ``user`` Database user. The database user that the backup should be executed as. Even if you list more than one database (below) all backups must be done as the same user. This value is optional. Consult your PostgreSQL documentation for information on how to configure a default database user outside of Cedar Backup, and for information on how to specify a database password when you configure a user within Cedar Backup. You will probably want to modify ``pg_hda.conf``. *Restrictions:* If provided, must be non-empty. ``compress_mode`` Compress mode. PostgreSQL databases dumps are just specially-formatted text files, and often compress quite well using ``gzip`` or ``bzip2``. The compress mode describes how the backed-up data will be compressed, if at all. *Restrictions:* Must be one of ``none``, ``gzip`` or ``bzip2``. ``all`` Indicates whether to back up all databases. If this value is ``Y``, then all PostgreSQL databases will be backed up. If this value is ``N``, then one or more specific databases must be specified (see below). If you choose this option, the entire database backup will go into one big dump file. *Restrictions:* Must be a boolean (``Y`` or ``N``). ``database`` Named database to be backed up. If you choose to specify individual databases rather than all databases, then each database will be backed up into its own dump file. This field can be repeated as many times as is necessary. At least one database must be configured if the all option (above) is set to ``N``. You may not configure any individual databases if the all option is set to ``Y``. *Restrictions:* Must be non-empty. .. _cedar-extensions-mbox: Mbox Extension -------------- The Mbox Extension is a Cedar Backup extension used to incrementally back up UNIX-style “mbox” mail folders via the Cedar Backup command line. It is intended to be run either immediately before or immediately after the standard collect action. Mbox mail folders are not well-suited to being backed up by the normal Cedar Backup incremental backup process. This is because active folders are typically appended to on a daily basis. This forces the incremental backup process to back them up every day in order to avoid losing data. This can result in quite a bit of wasted space when backing up large mail folders. The Mbox extension leverages the ``grepmail`` utility to back up only email messages which have been received since the last incremental backup. This way, even if a folder is added to every day, only the recently-added messages are is backed up. This can potentially save a lot of space. Each configured mbox file or directory can be backed using the same collect modes allowed for filesystems in the standard Cedar Backup collect action (weekly, daily, incremental) and the output can be compressed using either ``gzip`` or ``bzip2``. To enable this extension, add the following section to the Cedar Backup configuration file: :: mbox CedarBackup3.extend.mbox executeAction 99 This extension relies on the options and collect configuration sections in the standard Cedar Backup configuration file, and then also requires its own ``mbox`` configuration section. This is an example mbox configuration section: :: incr gzip /home/user1/mail/greylist daily /home/user2/mail /home/user3/mail spam .*debian.* Configuration is much like the standard collect action. Differences come from the fact that mbox directories are *not* collected recursively. Unlike collect configuration, exclusion information can only be configured at the mbox directory level (there are no global exclusions). Another difference is that no absolute exclusion paths are allowed --- only relative path exclusions and patterns. The following elements are part of the mbox configuration section: ``collect_mode`` Default collect mode. The collect mode describes how frequently an mbox file or directory is backed up. The mbox extension recognizes the same collect modes as the standard Cedar Backup collect action (see :doc:`basic`). This value is the collect mode that will be used by default during the backup process. Individual files or directories (below) may override this value. If *all* individual files or directories provide their own value, then this default value may be omitted from configuration. *Note:* if your backup device does not suppport multisession discs, then you should probably use the ``daily`` collect mode to avoid losing data. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``compress_mode`` Default compress mode. Mbox file or directory backups are just text, and often compress quite well using ``gzip`` or ``bzip2``. The compress mode describes how the backed-up data will be compressed, if at all. This value is the compress mode that will be used by default during the backup process. Individual files or directories (below) may override this value. If *all* individual files or directories provide their own value, then this default value may be omitted from configuration. *Restrictions:* Must be one of ``none``, ``gzip`` or ``bzip2``. ``file`` An individual mbox file to be collected. This is a subsection which contains information about an individual mbox file to be backed up. This section can be repeated as many times as is necessary. At least one mbox file or directory must be configured. The file subsection contains the following fields: ``collect_mode`` Collect mode for this file. This field is optional. If it doesn't exist, the backup will use the default collect mode. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``compress_mode`` Compress mode for this file. This field is optional. If it doesn't exist, the backup will use the default compress mode. *Restrictions:* Must be one of ``none``, ``gzip`` or ``bzip2``. ``abs_path`` Absolute path of the mbox file to back up. *Restrictions:* Must be an absolute path. ``dir`` An mbox directory to be collected. This is a subsection which contains information about an mbox directory to be backed up. An mbox directory is a directory containing mbox files. Every file in an mbox directory is assumed to be an mbox file. Mbox directories are *not* collected recursively. Only the files immediately within the configured directory will be backed-up and any subdirectories will be ignored. This section can be repeated as many times as is necessary. At least one mbox file or directory must be configured. The dir subsection contains the following fields: ``collect_mode`` Collect mode for this file. This field is optional. If it doesn't exist, the backup will use the default collect mode. *Restrictions:* Must be one of ``daily``, ``weekly`` or ``incr``. ``compress_mode`` Compress mode for this file. This field is optional. If it doesn't exist, the backup will use the default compress mode. *Restrictions:* Must be one of ``none``, ``gzip`` or ``bzip2``. ``abs_path`` Absolute path of the mbox directory to back up. *Restrictions:* Must be an absolute path. ``exclude`` List of paths or patterns to exclude from the backup. This is a subsection which contains a set of paths and patterns to be excluded within this mbox directory. This section is entirely optional, and if it exists can also be empty. The exclude subsection can contain one or more of each of the following fields: ``rel_path`` A relative path to be excluded from the backup. The path is assumed to be relative to the mbox directory itself. For instance, if the configured mbox directory is ``/home/user2/mail`` a configured relative path of ``SPAM`` would exclude the path ``/home/user2/mail/SPAM``. This field can be repeated as many times as is necessary. *Restrictions:* Must be non-empty. ``pattern`` A pattern to be excluded from the backup. The pattern must be a Python regular expression. It is assumed to be bounded at front and back by the beginning and end of the string (i.e. it is treated as if it begins with ``^`` and ends with ``$``). This field can be repeated as many times as is necessary. *Restrictions:* Must be non-empty .. _cedar-extensions-encrypt: Encrypt Extension ----------------- The Encrypt Extension is a Cedar Backup extension used to encrypt backups. It does this by encrypting the contents of a master's staging directory each day after the stage action is run. This way, backed-up data is encrypted both when sitting on the master and when written to disc. This extension must be run before the standard store action, otherwise unencrypted data will be written to disc. There are several differents ways encryption could have been built in to or layered on to Cedar Backup. I asked the mailing list for opinions on the subject in January 2007 and did not get a lot of feedback, so I chose the option that was simplest to understand and simplest to implement. If other encryption use cases make themselves known in the future, this extension can be enhanced or replaced. Currently, this extension supports only GPG. However, it would be straightforward to support other public-key encryption mechanisms, such as OpenSSL. |warning| If you decide to encrypt your backups, be *absolutely sure* that you have your GPG secret key saved off someplace safe --- someplace other than on your backup disc. If you lose your secret key, your backup will be useless. I suggest that before you rely on this extension, you should execute a dry run and make sure you can successfully decrypt the backup that is written to disc. Before configuring the Encrypt extension, you must configure GPG. Either create a new keypair or use an existing one. Determine which user will execute your backup (typically root) and have that user import *and lsign* the public half of the keypair. Then, save off the secret half of the keypair someplace safe, apart from your backup (i.e. on a floppy disk or USB drive). Make sure you know the recipient name associated with the public key because you'll need it to configure Cedar Backup. (If you can run ``gpg -e -r "Recipient Name" file.txt`` and it executes cleanly with no user interaction required, you should be OK.) An encrypted backup has the same file structure as a normal backup, so all of the instructions in :doc:`recovering` apply. The only difference is that encrypted files will have an additional ``.gpg`` extension (so for instance ``file.tar.gz`` becomes ``file.tar.gz.gpg``). To recover decrypted data, simply log on as a user which has access to the secret key and decrypt the ``.gpg`` file that you are interested in. Then, recover the data as usual. *Note:* I am being intentionally vague about how to configure and use GPG, because I do not want to encourage neophytes to blindly use this extension. If you do not already understand GPG well enough to follow the two paragraphs above, *do not use this extension*. Instead, before encrypting your backups, check out the excellent GNU Privacy Handbook at ``__ and gain an understanding of how encryption can help you or hurt you. To enable this extension, add the following section to the Cedar Backup configuration file: :: encrypt CedarBackup3.extend.encrypt executeAction 301 This extension relies on the options and staging configuration sections in the standard Cedar Backup configuration file, and then also requires its own ``encrypt`` configuration section. This is an example Encrypt configuration section: :: gpg Backup User The following elements are part of the Encrypt configuration section: ``encrypt_mode`` Encryption mode. This value specifies which encryption mechanism will be used by the extension. Currently, only the GPG public-key encryption mechanism is supported. *Restrictions:* Must be ``gpg``. ``encrypt_target`` Encryption target. The value in this field is dependent on the encryption mode. For the ``gpg`` mode, this is the name of the recipient whose public key will be used to encrypt the backup data, i.e. the value accepted by ``gpg -r``. .. _cedar-extensions-split: Split Extension --------------- The Split Extension is a Cedar Backup extension used to split up large files within staging directories. It is probably only useful in combination with the ``cback3-span`` command, which requires individual files within staging directories to each be smaller than a single disc. You would normally run this action immediately after the standard stage action, but you could also choose to run it by hand immediately before running ``cback3-span``. The split extension uses the standard UNIX ``split`` tool to split the large files up. This tool simply splits the files on bite-size boundaries. It has no knowledge of file formats. *Note: this means that in order to recover the data in your original large file, you must have every file that the original file was split into.* Think carefully about whether this is what you want. It doesn't sound like a huge limitation. However, ``cback3-span`` might put an indivdual file on *any* disc in a set --- the files split from one larger file will not necessarily be together. That means you will probably need every disc in your backup set in order to recover any data from the backup set. To enable this extension, add the following section to the Cedar Backup configuration file: :: split CedarBackup3.extend.split executeAction 299 This extension relies on the options and staging configuration sections in the standard Cedar Backup configuration file, and then also requires its own ``split`` configuration section. This is an example Split configuration section: :: 250 MB 100 MB The following elements are part of the Split configuration section: ``size_limit`` Size limit. Files with a size strictly larger than this limit will be split by the extension. You can enter this value in two different forms. It can either be a simple number, in which case the value is assumed to be in bytes; or it can be a number followed by a unit (KB, MB, GB). Valid examples are “10240”, “250 MB” or “1.1 GB”. *Restrictions:* Must be a size as described above. ``split_size`` Split size. This is the size of the chunks that a large file will be split into. The final chunk may be smaller if the split size doesn't divide evenly into the file size. You can enter this value in two different forms. It can either be a simple number, in which case the value is assumed to be in bytes; or it can be a number followed by a unit (KB, MB, GB). Valid examples are “10240”, “250 MB” or “1.1 GB”. *Restrictions:* Must be a size as described above. .. _cedar-extensions-capacity: Capacity Extension ------------------ The capacity extension checks the current capacity of the media in the writer and prints a warning if the media exceeds an indicated capacity. The capacity is indicated either by a maximum percentage utilized or by a minimum number of bytes that must remain unused. This action can be run at any time, but is probably best run as the last action on any given day, so you get as much notice as possible that your media is full and needs to be replaced. To enable this extension, add the following section to the Cedar Backup configuration file: :: capacity CedarBackup3.extend.capacity executeAction 299 This extension relies on the options and store configuration sections in the standard Cedar Backup configuration file, and then also requires its own ``capacity`` configuration section. This is an example Capacity configuration section that configures the extension to warn if the media is more than 95.5% full: :: 95.5 This example configures the extension to warn if the media has fewer than 16 MB free: :: 16 MB The following elements are part of the Capacity configuration section: ``max_percentage`` Maximum percentage of the media that may be utilized. You must provide either this value *or* the ``min_bytes`` value. *Restrictions:* Must be a floating point number between 0.0 and 100.0 ``min_bytes`` Minimum number of free bytes that must be available. You can enter this value in two different forms. It can either be a simple number, in which case the value is assumed to be in bytes; or it can be a number followed by a unit (KB, MB, GB). Valid examples are “10240”, “250 MB” or “1.1 GB”. You must provide either this value *or* the ``max_percentage`` value. *Restrictions:* Must be a byte quantity as described above. ---------- *Previous*: :doc:`config` • *Next*: :doc:`extenspec` .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/extenspec.rst0000644000000000000000000001247514567004737016122 0ustar00.. _cedar-extenspec: Extension Architecture Interface ================================ The Cedar Backup Extension Architecture Interface is the application programming interface used by third-party developers to write Cedar Backup extensions. This appendix briefly specifies the interface in enough detail for someone to succesfully implement an extension. Backup extensions are third-party pieces of code which extend Cedar Backup's functionality. Extensions can be invoked from the Cedar Backup command line and are allowed to place their configuration in Cedar Backup's configuration file. There is a one-to-one mapping between a command-line extended action and an extension function. The mapping is configured in the Cedar Backup configuration file using a section something like this: :: database foo bar 101 In this case, the action “database” has been mapped to the extension function ``foo.bar()``. Extension functions can take any actions they would like to once they have been invoked, but must abide by these rules: 1. Extensions must not write to ``stdout`` or ``stderr`` using functions such as ``print`` or ``sys.write``. 2. All logging must take place using the Python logging facility. Flow-of-control logging should happen on the ``CedarBackup3.log`` topic. Authors can assume that ERROR will always go to the terminal, that INFO and WARN will always be logged, and that DEBUG will be ignored unless debugging is enabled. 3. Any time an extension invokes a command-line utility, it must be done through the ``CedarBackup3.util.executeCommand`` function. This will help keep Cedar Backup safer from format-string attacks, and will make it easier to consistently log command-line process output. 4. Extensions may not return any value. 5. Extensions must throw a Python exception containing a descriptive message if processing fails. Extension authors can use their judgement as to what constitutes failure; however, any problems during execution should result in either a thrown exception or a logged message. 6. Extensions may rely only on Cedar Backup functionality that is advertised as being part of the public interface. This means that extensions must not directly make use of methods, functions or values starting with with the ``_`` character. Furthermore, extensions should only rely on parts of the public interface that are documented in the online interface documentation. 7. Extension authors are encouraged to extend the Cedar Backup public interface through normal methods of inheritence. However, no extension is allowed to directly change Cedar Backup code in a way that would affect how Cedar Backup itself executes when the extension has not been invoked. For instance, extensions would not be allowed to add new command-line options or new writer types. 8. Extensions must be written to assume an empty locale set (no ``$LC_*`` settings) and ``$LANG=C``. For the typical open-source software project, this would imply writing output-parsing code against the English localization (if any). The ``executeCommand`` function does sanitize the environment to enforce this configuration. Extension functions take three arguments: the path to configuration on disk, a ``CedarBackup3.cli.Options`` object representing the command-line options in effect, and a ``CedarBackup3.config.Config`` object representing parsed standard configuration. :: def function(configPath, options, config): """Sample extension function.""" pass This interface is structured so that simple extensions can use standard configuration without having to parse it for themselves, but more complicated extensions can get at the configuration file on disk and parse it again as needed. The interface to the ``CedarBackup3.cli.Options`` and ``CedarBackup3.config.Config`` classes has been thoroughly documented in the online interface documentation. The interface is guaranteed to change only in backwards-compatible ways unless the Cedar Backup major version number is bumped (i.e. from 3 to 4). If an extension needs to add its own configuration information to the Cedar Backup configuration file, this extra configuration must be added in a new configuration section using a name that does not conflict with standard configuration or other known extensions. For instance, our hypothetical database extension might require configuration indicating the path to some repositories to back up. This information might go into a section something like this: :: /path/to/repo1 /path/to/repo2 In order to read this new configuration, the extension code can either inherit from the ``Config`` object and create a subclass that knows how to parse the new ``database`` config section, or can write its own code to parse whatever it needs out of the file. Either way, the resulting code is completely independent of the standard Cedar Backup functionality. ---------- *Previous*: :doc:`extensions` • *Next*: :doc:`depends` .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/images/note.png0000644000000000000000000000317214567004737016304 0ustar00‰PNG  IHDR00Ř`nĐgAMAXÇüGŕ cHRMz%€ů˙€ću.ę_:—oiäÄ+IDATxśbř?Č@1 ´Đ@QŃA˙¨b @QĂAżźü÷˙ťýßĎ›ţRě*€˘ŘAżîü­wď0ĂöE ˙źł|xÜ÷ó7Ećeúuű˙kÝ×Wmx¸VĎ`ř˙‚ńýăIż˙’o$@Qŕ `L˝ŃşĆĘ”ˇ*‡që"Fa†ĺSţżäx÷t%Ůn rôűń˙÷÷O2Ř[2¦2ţĘř˙ă¦ů 2’ K&ÉçýÓżţc0@‘ĺ `şyŁ÷đ$¶:CGă˙WŚ˙2ţ»Çř˙5ă‰MŚJr +§ÉéíĂ^2Ň@‘î PşŃyu™ÁŢŠˇłěš ×@Đ˙7ŚűW‚ânĺ4 ›ČIOD˘ŇMqšk nzŸq.Ł ?4=˝'1=)Ąű'AaM7Ń]§Í éi1éé €vRşiGJ7¸JzzABz âN7Ż/3ŘY2tV §śnBNO ňi21q@D8tÓMŻ7ĚAJOϧ'€"ä ßŹ ĺ 0lŠđ¦„¸ €ĐˡwďÝ>Wö˙ Ci6(î.ďĺęşĆßť!ćá˙{†#D€atÍëׯá ,%ő»żîžĎĆ]I6¤8ŐŇ(Ý<ĄG†_O ®±ĘÉ-yôč!˛í„˝.{óöËÝ3‘ 7e1¨@ŇÓKꤠkţľ`¸N7@×ü†¤' ›$ĹHNO×@ÓÍS k„í éć.p›úÍŰĎČéé9Ńĺ$ÝřÓ ´Ľ±¦›âű÷ďă± €ęuĽxůţ.8=•e3h3Ü&"=AÂ&5Š!Ä›á÷S†‹{ř}˝,pĄd@ÄöË€ééÖŮrH™ JO»ńĄ'Hć©P¤ň¦°¸ WşADBĎ”ž.NO<I7żź"•7 „ i}{`ůtďL4=)bIOtŕÎŕ .o€éĆÎÖ ”nÜ'Ň €"yôžîÁÓjů ›”(PĄ›ÝŔtާ¦d@äŚAÓÓ¤ôô)Ýř‚ÓÍzHşÁYŢŕDć8=ŔÓ°M÷ď9,Ý<Ą;;ë\˘Ó 2 ňÇAéél$ÄMÚjŚ>ÎŚŢ.°z ”n°ÔSÄ€˘h”žÎ'ă®"TŢüyݧŞHJ7Č €(§~űţ׍3e˙î3üËph-–ö © €¨0’˙áăŻs‡k¦µ+ŮŰ™çćá«§DťąŽ×ożMťľxâÄ)IOĹh €¨9ôďć_ )°u!Y§ŮÖIEND®B`‚././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/images/tip.png0000644000000000000000000000443314567004737016134 0ustar00‰PNG  IHDR00Ř`nĐgAMAXÇüGŕ cHRMz%€ů˙€ću.ę_:—oiäÄ+¦IDATxśbř?Č@1 ´Đ@ :ůúýű÷÷ďßż~ýú ą@A ŮĆÉúńăÇ»wďžÁË—/ßż˙ńăÇO`dąŻ^˝‚Č•ýüů“Tó˝ţâĹ‹gĎžmúýű~Ĺ@@e@Ĺ@-@ŤÄŰ@D9čQ``M˙ţý\đůóo۶=ž8ńjmíŮÚÚs`tvâÄ+»v=yůá‚oßľBśőë?@@v0eĂHB¸7o~:őZPĐ^QŃe óf30ĚDB@î|1±ĺ{ĘnÝúŃŚP ł€qJĐ:€"ŕ 7oŢĂć˙˙@öÇ_˛˛Žńđ,Ű DóŔZŔŔ0—aÍ‹ĚKÔđó/)(8ÔÔţďßßGŹý†ßF€Âé ˙ţ“'Ř5 0wî-ié0w,@Bs44ÖžŞ¨8ceµě¸ůH˛@Ĺł€Ú!ć<~üřÉ“'x@8tĘŰ·o!쪪ł`›ć˘:ä?ż˝oŢü€(űőë/ĐYŕpBSÔ8§ąůD0îîßżŹË^€Âî `dż~ýú?(łü+.>IÖ̦•'Oľ"küű÷źĄĺVlnŐ\ Ç€•Ý˝{̱Z @XÉŢvnî pâŔt (x<=wcjݍ8 Ö‚©hČ, ˙ÁééÜąsŔŠ© €ĐL:Oź>…dďľľ«8Âę +«­&feťŔá ›fŤ*ˇűöíĂ,ÓÝAđČz€“sFF1ťcáŃŁ/‘µżző]^~5^]ó€Ćž= ›łgĎ^Ľẍ́⠿˙SÜź?@w˙suÝ \ćBS«šÚšŤ˝}űăăÇ_GŽĽtvŢ-ˇˇYAAű˙ÓĆşuë€E˛ĹAŔŇŐŚ={ž!3ÜT&"˛\\|Ól9Kвł/N\1Mˇf-jÖŻIII𲠀úňĺ $©Ďž}“>!´č?¸`ĚĎχw"á `Ň‚ľrĺ=;ű""J[J¬_-j™9sfcc#Ľ: „€M3`]lËýůóĎÁa;19źͶ·ß´rrr–,Y7 €Pj{`›Ňuš<ů:Ť!J4gÎP|]˝zŐßß˙Ę•+pĹAŔ*°ýúÔ1ř&$´Kć(+ŻýúTŐwwwgdd ÷µ˝ Ś5`ę2/ $ěRS#č Yíí—ţCnnnóćÍC6 €ĐLÚ>Fí§Ożôő7âÎ˙ŔŔ›ĂČ8˙޽ϨÚ˙ި¬ëÂşłôő7 *îěěôööľ}ű6˛ „ĄĚk†Ń… ďVcMÝŔ _]}-° d˝ŔrبĐĐXl11-ÄŚ, @c*Oź>mkk LJČÍE  ,°gůő+¨xţü[ ‰ĺ¨nš´éŔçŔ<‚©€RŻ_˙ŕç_ŠNs$%W üŞż^űřřóĽ’‡€ÂŢsşćüůóŔ6äPę%°‡Š”ž@Ú˛ĺ1°§ěôĽxń ŰĺoŢ|ż{÷˛fËËŻ‚¸Řô–„@űŠVÎľ=Đ@7A ŘCĐĐX mZŔŵŹo1//NÄĂłŘl‚Ą¶™@íp×´··ŰŮŮíÚµ «˝„o8ء޿?$= ‚“Ŕ0‡%Řy°‘\Ň -%µ¨¨ý?8 Ăt-ZLX- ăC>|ö›<¸á3yYŮ)yů•°1ˇą.›Np ) ˛ŞŞ3Źč˝~ýşźźź——×Îť;Ń22 Â#hŔXŰşuë¦M› AőÔ.řą~ýì¬ă&&›„…—sp,†# ŘĘÉÉ9TTQ˙ńㇹsç:::ĆÄÄś8qżuDÔ#°Ëě©L›6măĆŤŻ^!:óŔPóćçÍ›źnÝ! ČEÖ¬‹€ýŔ   ›¦¦¦´ €H…Fß–-[şşş€őó¶mŰ®]»úáĂ»˙І/ţ~úô;ŔÖáŔÜTWWwřđa`‘KŚ-Dň85°,ćľeË–ŐÖÖ¦ĄĄçAAAPXYúúú†‡‡ĄÖ¬Y)÷‰7 €Čɦ­»wď9rdőęŐ‹a؆ÍŮłgMR" Đ ›ë Aç €T”mőŚĺGŞIEND®B`‚././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/images/warning.png0000644000000000000000000000301614567004737017001 0ustar00‰PNG  IHDR00Ř`nĐgAMAXÇüGŕ cHRMz%€ů˙€ću.ę_:—oiäÄ+™IDATxśbř?Č@1 ´Đ@ :9úóĺËÇ3gž-Yr·±ńjRŇĺ¨(8şYTô°ŻďÍŽß="ĎADš>]Ľx=7÷°ŠĘnVÖť ۶cC»öńňžőđx:w_I˛ €uĐ—«W/EGďáŕÚtĘ.°­¸Đ.0ÚvÜQ-­Ç3gţýő‹H‹°ţýúu·ˇa?˙vBîŔŠv‚]vŇÚúýńăÄ8 €8čŰÝ»BBČs 2š°OHčá¬Y˙ýĂo#@ásĐÇ“'ÉËo§Ě)Čń Şó©©?ľ}Ăc)@átĐÇS§ö kędxřWÜn ěúöŕÁ!™Ôv ÜM§˛łăp@aqĐß?.R+¦°˘LLçćĚůÍA„ĹAŔ⎦® M||÷íĂ´ €ĐôőćÍ˝ĽĽć)"Ń;»ÇŻ_Ł9 €Đt9"‚ÁD{¶00lîëCKL„â /W®ěag§OđŃ>†ÚÚoßFv@ˇ8čFnî6ząHë–ööţEr@!ô÷Ű·# T/x˘Ď?}‚; €¶(ö01AâkŽjśZîí@ÉË;sî €B8Řľ¨’GUUŻ&&^MH  JJ:meµ –Śćsr.3î €B8čn}=$}p-9ů?-°ť©ö20¬c`¨+*úüý;D €:|;ĚA@ĐÔAOfÎÜK×›JCBż| ‘ „.GFŇĎA3fŔ´•!ÇĹĺŇÍ›)€BrPTÔ€8NŇěíO]Ľ‘ Áá ‡Ó—.A¤h࣠ÍA„pĐÍ‚D.KIˇ©žÍ›‡ś¨Ł\]ĎŔ˘ €z8a$„@ MOĎ÷‡Ń >|«¤d'¬ZÉÂtăÖ-3á ×;vě†7ćĄ6íĐ|HI}a2/ojZÚ«7o Î „€}Í}\\đŞ~-Ľ"; ,ĹÄŞZZ~Á:n„RŰźqv¦Q;Wm,©tt¬\ w@ˇ8č),­Ńíg`XĚÎîďăséʸĹA€-eeşµ@10d)(d——‡Ud@@čMذ‚ÖŘđXĘÉédm˝bŐ*dş~ýřqÔÄ„ÖnÚvP˘šZTJʓǏ‘@XşA/Ţ% @Ó–50·w‰9şąmÜ´ Ív€ÂŢs˝>aí tss;XYµ÷ô § ěúţóçÁ°0Z¸ 6ŮŘôőÓrsź>}Ši5@álť;ÜÝw€ă›ŠatM¨–V@LĚEXĺ…ßpĚÓWݶEDledÜK±Sö€Ăf1O€žžtôýűqY @¬Ţýř±±ˇa Ç> ‚ęu‹Ű[Y%ee]8ŹŤDxHďÓŻ_ćÎť®Łłl.ńˇÉŰŔhZĘÍ ĚávÎÎŤ­­>Äo@5č ě~?~R^ŢD9ąőŕ6ĂApÁżŚö ˇ˝`ŮŕZČ^ÂÉ™­¨hoi»lĹŠ/_ľ´ €H~˙ýű–={: ZÍĚúĹÄ河Ż7Ń‘{€Ŕ·Š•u*_˝¸x¬––§«kbNάąsďÜąC¤-DňŔů·ßż/Ţął|íÚĆşş´¨¨d'§x ‹SÓ3łX3ł@kk_7·đĚââľéÓ=úáý{’Ě ň§€eŐŁ§OĎ^ľĽóŔÍ»vmŮ˝¶íÚuěĉ;÷î}BꮓhĐÍuĐ s@€ŰOěßo–CIEND®B`‚././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/index.rst0000644000000000000000000000036014567004737015221 0ustar00Cedar Backup v3 Software Manual =============================== .. toctree:: :maxdepth: 2 preface intro basic install commandline config extensions extenspec depends recovering securingssh copyright ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/install.rst0000644000000000000000000001143714567004737015567 0ustar00.. _cedar-install: Installation ============ .. _cedar-install-background: Background ---------- There are two different ways to install Cedar Backup. The easiest way is to install the pre-built Debian packages. This method is painless and ensures that all of the correct dependencies are available, etc. If you are running a Linux distribution other than Debian or you are running some other platform like FreeBSD or Mac OS X, then you must install the python package. When using the Python package, you need to manage all of the external dependencies yourself. Cedar Backup has been developed on a Debian GNU/Linux system and is primarily supported on Debian and other Linux systems. However, since it is written in portable Python 3, it should run without problems on just about any UNIX-like operating system. In particular, full Cedar Backup functionality is known to work on Debian and SuSE Linux systems, and client functionality is also known to work on FreeBSD and Mac OS X systems. To run a Cedar Backup client, you really just need a working Python 3 installation. To run a Cedar Backup master, you will also need a set of other executables, most of which are related to building and writing CD/DVD images. A full list of dependencies is provided further on in this chapter. .. _cedar-install-debian: Installing on a Debian System ----------------------------- The easiest way to install Cedar Backup onto a Debian system is by using a tool such as ``apt-get`` or ``aptitude``. Install Cedar Backup using this set of commands: :: $ apt-get update $ apt-get install cedar-backup3 cedar-backup3-doc Several of the Cedar Backup dependencies are listed as “recommended” rather than required. If you are installing Cedar Backup on a master machine, you must install some or all of the recommended dependencies, depending on which actions you intend to execute. The stage action normally requires *ssh*, and the store action requires *eject* and either *cdrecord*/*mkisofs* or *dvd+rw-tools*. Clients must also install some sort of SSH server if a remote master will collect backups from them. Once the package has been installed, you can proceed to configuration as described in :doc:`config`. |note| The Debian package-management tools must generally be run as root. It is safe to install Cedar Backup to a non-standard location and run it as a non-root user. However, to do this, you must install the Python package instead of the Debian package. .. _cedar-install-python: Installing the Python Package ----------------------------- On platforms other than Debian, Cedar Backup is installed as a Python package. You will have to manage external dependencies on your own. The easiest way to install the Python package is using Pip:: $ pip install cedar-backup3 There are other ways to install a package, but this is the simplest and is probably the mechanism most people will want to use. If you are an experienced Python user, feel free to use whichever mechanism you prefer on your own system. .. _cedar-install-python-deps: Installing Dependencies ~~~~~~~~~~~~~~~~~~~~~~~ Cedar Backup requires a number of external packages in order to function properly. Before installing Cedar Backup, you must make sure that these dependencies are met. Cedar Backup is written in Python 3 and requires version 3.8 or greater of the language. Additionally, remote client peer nodes must be running an RSH-compatible server, such as the ``ssh`` server, and master nodes must have an RSH-compatible client installed if they need to connect to remote peer machines. Master machines also require several other system utilities, most having to do with writing and validating CD/DVD media. On master machines, you must make sure that these utilities are available if you want to to run the store action: - ``mkisofs`` - ``eject`` - ``mount`` - ``unmount`` - ``volname`` Then, you need this utility if you are writing CD media: - ``cdrecord`` *or* these utilities if you are writing DVD media: - ``growisofs`` All of these utilities are common and are easy to find for almost any UNIX-like operating system. |tip| Many UNIX-like distributions provide an automatic or semi-automatic way to install packages like the ones Cedar Backup requires (think RPMs for RedHat, Gentoo's Portage system, Homebrew for Mac, or the BSD ports system). If you are not sure how to install these packages on your system, you might want to check out :doc:`depends`. This appendix provides links to “upstream” source packages, plus as much information as I have been able to gather about packages for non-Debian platforms. ---------- *Previous*: :doc:`basic` • *Next*: :doc:`commandline` ---------- .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/intro.rst0000644000000000000000000002472114567004737015254 0ustar00.. _cedar-intro: Introduction ============ “Only wimps use tape backup: real men just upload their important stuff on ftp, and let the rest of the world mirror it.”--- Linus Torvalds, at the release of Linux 2.0.8 in July of 1996. .. _cedar-intro-whatis: What is Cedar Backup? --------------------- Cedar Backup is a software package designed to manage system backups for a pool of local and remote machines. Cedar Backup understands how to back up filesystem data as well as MySQL and PostgreSQL databases and Subversion repositories. It can also be easily extended to support other kinds of data sources. Cedar Backup is focused around weekly backups to a single CD or DVD disc, with the expectation that the disc will be changed or overwritten at the beginning of each week. If your hardware is new enough (and almost all hardware is today), Cedar Backup can write multisession discs, allowing you to add incremental data to a disc on a daily basis. Alternately, Cedar Backup can write your backups to the Amazon S3 cloud rather than relying on physical media. Besides offering command-line utilities to manage the backup process, Cedar Backup provides a well-organized library of backup-related functionality, written in the Python 3 programming language. There are many different backup software systems in the open source world. Cedar Backup aims to fill a niche: it aims to be a good fit for people who need to back up a limited amount of important data on a regular basis. Cedar Backup isn’t for you if you want to back up your huge MP3 collection every night, or if you want to back up a few hundred machines. However, if you administer a small set of machines and you want to run daily incremental backups for things like system configuration, current email, small web sites, source code repositories, or small databases, then Cedar Backup is probably worth your time. Cedar Backup has been developed on a Debian GNU/Linux system and is primarily supported on Debian and other Linux systems. However, since it is written in portable Python 3, it should run without problems on just about any UNIX-like operating system. In particular, full Cedar Backup functionality is known to work on Debian and SuSE Linux systems, and client functionality is also known to work on FreeBSD and Mac OS X systems. To run a Cedar Backup client, you really just need a working Python 3 installation. To run a Cedar Backup master, you will also need a set of other executables, most of which are related to building and writing CD/DVD images or talking to the Amazon S3 infrastructure. A full list of dependencies is provided in :doc:`install`. .. _cedar-intro-migrating: Migrating from Version 2 to Version 3 ------------------------------------- The main difference between Cedar Backup version 2 and Cedar Backup version 3 is the targeted Python interpreter. Cedar Backup version 2 was designed for Python 2, while version 3 is a conversion of the original code to Python 3. Other than that, both versions are functionally equivalent. The configuration format is unchanged, and you can mix-and-match masters and clients of different versions in the same backup pool. Python 2 has now reached its end of life, and Cedar Backup v2 has been unsupported since 11 Nov 2017. A major design goal for version 3 was to facilitate easy migration testing for users, by making it possible to install version 3 on the same server where version 2 was already in use. A side effect of this design choice is that all of the executables, configuration files, and logs changed names in version 3. Where version 2 used ``cback``, version 3 uses ``cback3``: ``cback3.conf`` instead of ``cback.conf``, ``cback3.log`` instead of ``cback.log``, etc. So, while migrating from version 2 to version 3 is relatively straightforward, you will have to make some changes manually. You will need to create a new configuration file (or soft link to the old one), modify your cron jobs to use the new executable name, etc. You can migrate one server at a time in your pool with no ill effects, or even incrementally migrate a single server by using version 2 and version 3 on different days of the week or for different parts of the backup. .. _cedar-intro-support: How to Get Support ------------------ Cedar Backup is open source software that is provided to you at no cost. It is provided with no warranty, not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. However, that said, someone can usually help you solve whatever problems you might see. If you experience a problem, your best bet is to file an issue in the issue tracker at GitHub. [1]_ When the source code was hosted at SourceForge, there was a mailing list. However, it was very lightly used in the last years before I abandoned SourceForge, and I have decided not to replace it. If you are not comfortable discussing your problem in public or listing it in a public database, or if you need to send along information that you do not want made public, then you can write support@cedar-solutions.com. That mail will go directly to me. If you write the support address about a bug, a “scrubbed” bug report will eventually end up in the public bug database anyway, so if at all possible you should use the public reporting mechanisms. One of the strengths of the open-source software development model is its transparency. Regardless of how you report your problem, please try to provide as much information as possible about the behavior you observed and the environment in which the problem behavior occurred. [2]_ In particular, you should provide: the version of Cedar Backup that you are using; how you installed Cedar Backup (i.e. Debian package, source package, etc.); the exact command line that you executed; any error messages you received, including Python stack traces (if any); and relevant sections of the Cedar Backup log. It would be even better if you could describe exactly how to reproduce the problem, for instance by including your entire configuration file and/or specific information about your system that might relate to the problem. However, please do *not* provide huge sections of debugging logs unless you are sure they are relevant or unless someone asks for them. |tip| Sometimes, the error that Cedar Backup displays can be rather cryptic. This is because under internal error conditions, the text related to an exception might get propogated all of the way up to the user interface. If the message you receive doesn't make much sense, or if you suspect that it results from an internal error, you might want to re-run Cedar Backup with the ``--stack`` option. This forces Cedar Backup to dump the entire Python stack trace associated with the error, rather than just printing the last message it received. This is good information to include along with a bug report, as well. .. _cedar-intro-history: History ------- Cedar Backup began life in late 2000 as a set of Perl scripts called kbackup. These scripts met an immediate need (which was to back up skyjammer.com and some personal machines) but proved to be unstable, overly verbose and rather difficult to maintain. In early 2002, work began on a rewrite of kbackup. The goal was to address many of the shortcomings of the original application, as well as to clean up the code and make it available to the general public. While doing research related to code I could borrow or base the rewrite on, I discovered that there was already an existing backup package with the name kbackup, so I decided to change the name to Cedar Backup instead. Because I had become fed up with the prospect of maintaining a large volume of Perl code, I decided to abandon that language in favor of Python. At the time, I chose Python mostly because I was interested in learning it, but in retrospect it turned out to be a very good decision. Around this same time, skyjammer.com and cedar-solutions.com were converted to run Debian GNU/Linux and I entered the Debian new maintainer queue, so I also made it a goal to implement Debian packages along with a Python source distribution for the new release. Version 1.0 of Cedar Backup was released in June of 2002. We immediately began using it to back up skyjammer.com and cedar-solutions.com, where it proved to be much more stable than the original code. In the meantime, I continued to improve as a Python programmer and also started doing a significant amount of professional development in Java. It soon became obvious that the internal structure of Cedar Backup 1.0, while much better than kbackup, still left something to be desired. In November 2003, I began an attempt at cleaning up the codebase. I converted all of the internal documentation to use Epydoc, and updated the code to use the newly-released Python logging package after having a good experience with Java's log4j. However, I was still not satisfied with the code, which did not lend itself to the automated regression testing I had used when working with JUnit in my Java code. So, rather than releasing the cleaned-up code, I instead began another ground-up rewrite in May 2004. With this rewrite, I applied everything I had learned from other Java and Python projects I had undertaken over the last few years. I structured the code to take advantage of Python's unique ability to blend procedural code with object-oriented code, and I made automated unit testing a primary requirement. The result was the 2.0 release, which was cleaner, more compact, better focused, and better documented than any release before it. Utility code is less application-specific, and is now usable as a general-purpose library. The 2.0 release also includes a complete regression test suite of over 3800 tests, which will help to ensure that quality is maintained as development continues into the future. The 3.0 release of Cedar Backup is a Python 3 conversion of the 2.0 release, with minimal additional functionality. The conversion from Python 2 to Python 3 started in mid-2015, about 5 years before the anticipated deprecation of Python 2 in 2020. In 2020, the Python package structure, development tooling, and documentation format were modernized, preparing Cedar Backup for the next phase of its life. ---------- *Previous*: :doc:`preface` • *Next*: :doc:`basic` ---------- .. [1] See ``__ .. [2] See Simon Tatham's excellent bug reporting tutorial: ``__ . .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/preface.rst0000644000000000000000000000577414567004737015535 0ustar00.. _cedar-preface: Preface ======= .. _cedar-preface-purpose: Purpose ------- This software manual has been written to document version 2 and version 3 of Cedar Backup. Version 2 was first released in 2005, and version 3 in 2015. .. _cedar-preface-audience: Audience -------- This manual has been written for computer-literate administrators who need to use and configure Cedar Backup on their Linux or UNIX-like system. The examples in this manual assume the reader is relatively comfortable with UNIX and command-line interfaces. .. _cedar-preface-conventions: Conventions Used in This Book ----------------------------- This section covers the various conventions used in this manual. Typographic Conventions ~~~~~~~~~~~~~~~~~~~~~~~ *Term* Used for first use of important terms. ``Command`` Used for commands, command output, and switches *Replaceable* Used for replaceable items in code and text ``Filenames`` Used for file and directory names .. _cedar-preface-conventions-typo: Icons ~~~~~ |note| This icon designates a note relating to the surrounding text. |tip| This icon designates a helpful tip relating to the surrounding text. |warning| This icon designates a warning relating to the surrounding text. .. _cedar-preface-organization: Organization of This Manual --------------------------- :doc:`intro` Provides some some general history about Cedar Backup, what needs it is intended to meet, how to get support, and how to migrate from version 2 to version 3. :doc:`basic`: Discusses the basic concepts of a Cedar Backup infrastructure, and specifies terms used throughout the rest of the manual. :doc:`install` Explains how to install Cedar Backup either from the Debian package or from the Python package. :doc:`commandline`: Discusses the various Cedar Backup command-line tools, including the primary ``cback3`` command. :doc:`config`: Provides detailed information about how to configure Cedar Backup. :doc:`extensions` Describes each of the officially-supported Cedar Backup extensions. :doc:`extenspec` Specifies the Cedar Backup extension architecture interface, through which third party developers can write extensions to Cedar Backup. :doc:`depends` Provides some additional information about the packages which Cedar Backup relies on, including information about how to find documentation and packages on non-Debian systems. :doc:`recovering` Cedar Backup provides no facility for restoring backups, assuming the administrator can handle this infrequent task. This appendix provides some notes for administrators to work from. :doc:`securingssh` Password-less SSH connections are a necessary evil when remote backup processes need to execute without human interaction. This appendix describes some ways that you can reduce the risk to your backup pool should your master machine be compromised. *Next*: :doc:`intro` .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/recovering.rst0000644000000000000000000005152314567004737016264 0ustar00.. _cedar-recovering: Data Recovery ============= .. _cedar-recovering-finding: Finding your Data ----------------- The first step in data recovery is finding the data that you want to recover. You need to decide whether you are going to to restore off backup media, or out of some existing staging data that has not yet been purged. The only difference is, if you purge staging data less frequently than once per week, you might have some data available in the staging directories which would not be found on your backup media, depending on how you rotate your media. (And of course, if your system is trashed or stolen, you probably will not have access to your old staging data in any case.) Regardless of the data source you choose, you will find the data organized in the same way. The remainder of these examples will work off an example backup disc, but the contents of the staging directory will look pretty much like the contents of the disc, with data organized first by date and then by backup peer name. This is the root directory of my example disc: :: root:/mnt/cdrw# ls -l total 4 drwxr-x--- 3 backup backup 4096 Sep 01 06:30 2005/ In this root directory is one subdirectory for each year represented in the backup. In this example, the backup represents data entirely from the year 2005. If your configured backup week happens to span a year boundary, there would be two subdirectories here (for example, one for 2005 and one for 2006). Within each year directory is one subdirectory for each month represented in the backup. :: root:/mnt/cdrw/2005# ls -l total 2 dr-xr-xr-x 6 root root 2048 Sep 11 05:30 09/ In this example, the backup represents data entirely from the month of September, 2005. If your configured backup week happens to span a month boundary, there would be two subdirectories here (for example, one for August 2005 and one for September 2005). Within each month directory is one subdirectory for each day represented in the backup. :: root:/mnt/cdrw/2005/09# ls -l total 8 dr-xr-xr-x 5 root root 2048 Sep 7 05:30 07/ dr-xr-xr-x 5 root root 2048 Sep 8 05:30 08/ dr-xr-xr-x 5 root root 2048 Sep 9 05:30 09/ dr-xr-xr-x 5 root root 2048 Sep 11 05:30 11/ Depending on how far into the week your backup media is from, you might have as few as one daily directory in here, or as many as seven. Within each daily directory is a stage indicator (indicating when the directory was staged) and one directory for each peer configured in the backup: :: root:/mnt/cdrw/2005/09/07# ls -l total 10 dr-xr-xr-x 2 root root 2048 Sep 7 02:31 host1/ -r--r--r-- 1 root root 0 Sep 7 03:27 cback.stage dr-xr-xr-x 2 root root 4096 Sep 7 02:30 host2/ dr-xr-xr-x 2 root root 4096 Sep 7 03:23 host3/ In this case, you can see that my backup includes three machines, and that the backup data was staged on September 7, 2005 at 03:27. Within the directory for a given host are all of the files collected on that host. This might just include tarfiles from a normal Cedar Backup collect run, and might also include files “collected” from Cedar Backup extensions or by other third-party processes on your system. :: root:/mnt/cdrw/2005/09/07/host1# ls -l total 157976 -r--r--r-- 1 root root 11206159 Sep 7 02:30 boot.tar.bz2 -r--r--r-- 1 root root 0 Sep 7 02:30 cback.collect -r--r--r-- 1 root root 3199 Sep 7 02:30 dpkg-selections.txt.bz2 -r--r--r-- 1 root root 908325 Sep 7 02:30 etc.tar.bz2 -r--r--r-- 1 root root 389 Sep 7 02:30 fdisk-l.txt.bz2 -r--r--r-- 1 root root 1003100 Sep 7 02:30 ls-laR.txt.bz2 -r--r--r-- 1 root root 19800 Sep 7 02:30 mysqldump.txt.bz2 -r--r--r-- 1 root root 4133372 Sep 7 02:30 opt-local.tar.bz2 -r--r--r-- 1 root root 44794124 Sep 8 23:34 opt-public.tar.bz2 -r--r--r-- 1 root root 30028057 Sep 7 02:30 root.tar.bz2 -r--r--r-- 1 root root 4747070 Sep 7 02:30 svndump-0:782-opt-svn-repo1.txt.bz2 -r--r--r-- 1 root root 603863 Sep 7 02:30 svndump-0:136-opt-svn-repo2.txt.bz2 -r--r--r-- 1 root root 113484 Sep 7 02:30 var-lib-jspwiki.tar.bz2 -r--r--r-- 1 root root 19556660 Sep 7 02:30 var-log.tar.bz2 -r--r--r-- 1 root root 14753855 Sep 7 02:30 var-mail.tar.bz2 As you can see, I back up variety of different things on host1. I run the normal collect action, as well as the sysinfo, mysql and subversion extensions. The resulting backup files are named in a way that makes it easy to determine what they represent. Files of the form ``*.tar.bz2`` represent directories backed up by the collect action. The first part of the name (before “.tar.bz2”), represents the path to the directory. For example, ``boot.tar.gz`` contains data from ``/boot``, and ``var-lib-jspwiki.tar.bz2`` contains data from ``/var/lib/jspwiki``. The ``fdisk-l.txt.bz2``, ``ls-laR.tar.bz2`` and ``dpkg-selections.tar.bz2`` files are produced by the sysinfo extension. The ``mysqldump.txt.bz2`` file is produced by the mysql extension. It represents a system-wide database dump, because I use the “all” flag in configuration. If I were to configure Cedar Backup to dump individual datbases, then the filename would contain the database name (something like ``mysqldump-bugs.txt.bz2``). Finally, the files of the form ``svndump-*.txt.bz2`` are produced by the subversion extension. There is one dump file for each configured repository, and the dump file name represents the name of the repository and the revisions in that dump. So, the file ``svndump-0:782-opt-svn-repo1.txt.bz2`` represents revisions 0-782 of the repository at ``/opt/svn/repo1``. You can tell that this file contains a full backup of the repository to this point, because the starting revision is zero. Later incremental backups would have a non-zero starting revision, i.e. perhaps 783-785, followed by 786-800, etc. .. _cedar-recovering-filesystem: Recovering Filesystem Data -------------------------- Filesystem data is gathered by the standard Cedar Backup collect action. This data is placed into files of the form ``*.tar``. The first part of the name (before “.tar”), represents the path to the directory. For example, ``boot.tar`` would contain data from ``/boot``, and ``var-lib-jspwiki.tar`` would contain data from ``/var/lib/jspwiki``. (As a special case, data from the root directory would be placed in ``-.tar``). Remember that your tarfile might have a bzip2 (``.bz2``) or gzip (``.gz``) extension, depending on what compression you specified in configuration. If you are using full backups every day, the latest backup data is always within the latest daily directory stored on your backup media or within your staging directory. If you have some or all of your directories configured to do incremental backups, then the first day of the week holds the full backups and the other days represent incremental differences relative to that first day of the week. If you are restoring a home directory or some other non-system directory as part of a full restore, it is probably fine to extract the backup directly into the filesystem. If you are restoring a system directory like ``/etc`` as part of a full restore, extracting directly into the filesystem is likely to break things, especially if you re-installed a newer version of your operating system than the one you originally backed up. It's better to extract directories like this to a temporary location and pick out only the files you find you need. When doing a partial restore, I suggest *always* extracting to a temporary location. Doing it this way gives you more control over what you restore, and helps you avoid compounding your original problem with another one (like overwriting the wrong file, oops). .. _cedar-recovering-filesystem-full: Full Restore ------------ To do a full system restore, find the newest applicable full backup and extract it. If you have some incremental backups, extract them into the same place as the full backup, one by one starting from oldest to newest. (This way, if a file changed every day you will always get the latest one.) All of the backed-up files are stored in the tar file in a relative fashion, so you can extract from the tar file either directly into the filesystem, or into a temporary location. For example, to restore ``boot.tar.bz2`` directly into ``/boot``, execute ``tar`` from your root directory (``/``): :: root:/# bzcat boot.tar.bz2 | tar xvf - Of course, use ``zcat`` or just ``cat``, depending on what kind of compression is in use. If you want to extract ``boot.tar.gz`` into a temporary location like ``/tmp/boot`` instead, just change directories first. In this case, you'd execute the ``tar`` command from within ``/tmp`` instead of ``/``. :: root:/tmp# bzcat boot.tar.bz2 | tar xvf - Again, use ``zcat`` or just ``cat`` as appropriate. For more information, you might want to check out the manpage or GNU info documentation for the ``tar`` command. .. _cedar-recovering-filesystem-partial: Partial Restore --------------- Most users will need to do a partial restore much more frequently than a full restore. Perhaps you accidentally removed your home directory, or forgot to check in some version of a file before deleting it. Or, perhaps the person who packaged Apache for your system blew away your web server configuration on upgrade (it happens). The solution to these and other kinds of problems is a partial restore (assuming you've backed up the proper things). The procedure is similar to a full restore. The specific steps depend on how much information you have about the file you are looking for. Where with a full restore, you can confidently extract the full backup followed by each of the incremental backups, this might not be what you want when doing a partial restore. You may need to take more care in finding the right version of a file --- since the same file, if changed frequently, would appear in more than one backup. Start by finding the backup media that contains the file you are looking for. If you rotate your backup media, and your last known “contact” with the file was a while ago, you may need to look on older media to find it. This may take some effort if you are not sure when the change you are trying to correct took place. Once you have decided to look at a particular piece of backup media, find the correct peer (host), and look for the file in the full backup: :: root:/tmp# bzcat boot.tar.bz2 | tar tvf - path/to/file Of course, use ``zcat`` or just ``cat``, depending on what kind of compression is in use. The ``tvf`` tells ``tar`` to search for the file in question and just list the results rather than extracting the file. Note that the filename is relative (with no starting ``/``). Alternately, you can omit the ``path/to/file`` and search through the output using ``more`` or ``less`` If you haven't found what you are looking for, work your way through the incremental files for the directory in question. One of them may also have the file if it changed during the course of the backup. Or, move to older or newer media and see if you can find the file there. Once you have found your file, extract it using ``xvf``: :: root:/tmp# bzcat boot.tar.bz2 | tar xvf - path/to/file Again, use ``zcat`` or just ``cat`` as appropriate. Inspect the file and make sure it's what you're looking for. Again, you may need to move to older or newer media to find the exact version of your file. For more information, you might want to check out the manpage or GNU info documentation for the ``tar`` command. .. _cedar-recovering-mysql: Recovering MySQL Data --------------------- MySQL data is gathered by the Cedar Backup mysql extension. This extension always creates a full backup each time it runs. This wastes some space, but makes it easy to restore database data. The following procedure describes how to restore your MySQL database from the backup. |warning| I am not a MySQL expert. I am providing this information for reference. I have tested these procedures on my own MySQL installation; however, I only have a single database for use by Bugzilla, and I may have misunderstood something with regard to restoring individual databases as a user other than root. If you have any doubts, test the procedure below before relying on it! MySQL experts and/or knowledgable Cedar Backup users: feel free to write me and correct any part of this procedure. First, find the backup you are interested in. If you have specified “all databases” in configuration, you will have a single backup file, called ``mysqldump.txt``. If you have specified individual databases in configuration, then you will have files with names like ``mysqldump-database.txt`` instead. In either case, your file might have a ``.gz`` or ``.bz2`` extension depending on what kind of compression you specified in configuration. If you are restoring an “all databases” backup, make sure that you have correctly created the root user and know its password. Then, execute: :: daystrom:/# bzcat mysqldump.txt.bz2 | mysql -p -u root Of course, use ``zcat`` or just ``cat``, depending on what kind of compression is in use. Because the database backup includes ``CREATE DATABASE`` SQL statements, this command should take care of creating all of the databases within the backup, as well as populating them. If you are restoring a backup for a specific database, you have two choices. If you have a root login, you can use the same command as above: :: daystrom:/# bzcat mysqldump-database.txt.bz2 | mysql -p -u root Otherwise, you can create the database and its login first (or have someone create it) and then use a database-specific login to execute the restore: :: daystrom:/# bzcat mysqldump-database.txt.bz2 | mysql -p -u user database Again, use ``zcat`` or just ``cat`` as appropriate. For more information on using MySQL, see the documentation on the MySQL web site, ``__, or the manpages for the ``mysql`` and ``mysqldump`` commands. .. _cedar-recovering-subversion: Recovering Subversion Data -------------------------- Subversion data is gathered by the Cedar Backup subversion extension. Cedar Backup will create either full or incremental backups, but the procedure for restoring is the same for both. Subversion backups are always taken on a per-repository basis. If you need to restore more than one repository, follow the procedures below for each repository you are interested in. First, find the backup or backups you are interested in. Typically, you will need the full backup from the first day of the week and each incremental backup from the other days of the week. The subversion extension creates files of the form ``svndump-*.txt``. These files might have a ``.gz`` or ``.bz2`` extension depending on what kind of compression you specified in configuration. There is one dump file for each configured repository, and the dump file name represents the name of the repository and the revisions in that dump. So, the file ``svndump-0:782-opt-svn-repo1.txt.bz2`` represents revisions 0-782 of the repository at ``/opt/svn/repo1``. You can tell that this file contains a full backup of the repository to this point, because the starting revision is zero. Later incremental backups would have a non-zero starting revision, i.e. perhaps 783-785, followed by 786-800, etc. Next, if you still have the old Subversion repository around, you might want to just move it off (rename the top-level directory) before executing the restore. Or, you can restore into a temporary directory and rename it later to its real name once you've checked it out. That is what my example below will show. Next, you need to create a new Subversion repository to hold the restored data. This example shows an FSFS repository, but that is an arbitrary choice. You can restore from an FSFS backup into a FSFS repository or a BDB repository. The Subversion dump format is “backend-agnostic”. :: root:/tmp# svnadmin create --fs-type=fsfs testrepo Next, load the full backup into the repository: :: root:/tmp# bzcat svndump-0:782-opt-svn-repo1.txt.bz2 | svnadmin load testrepo Of course, use ``zcat`` or just ``cat``, depending on what kind of compression is in use. Follow that with loads for each of the incremental backups: :: root:/tmp# bzcat svndump-783:785-opt-svn-repo1.txt.bz2 | svnadmin load testrepo root:/tmp# bzcat svndump-786:800-opt-svn-repo1.txt.bz2 | svnadmin load testrepo Again, use ``zcat`` or just ``cat`` as appropriate. When this is done, your repository will be restored to the point of the last commit indicated in the svndump file (in this case, to revision 800). |note| *Note:* don't be surprised if, when you test this, the restored directory doesn't have exactly the same contents as the original directory. I can't explain why this happens, but if you execute ``svnadmin dump`` on both old and new repositories, the results are identical. This means that the repositories do contain the same content. For more information on using Subversion, see the book Version Control with Subversion (``__) or the Subversion FAQ (``__). .. _cedar-recovering-mbox: Recovering Mailbox Data ----------------------- Mailbox data is gathered by the Cedar Backup mbox extension. Cedar Backup will create either full or incremental backups, but both kinds of backups are treated identically when restoring. Individual mbox files and mbox directories are treated a little differently, since individual files are just compressed, but directories are collected into a tar archive. First, find the backup or backups you are interested in. Typically, you will need the full backup from the first day of the week and each incremental backup from the other days of the week. The mbox extension creates files of the form ``mbox-*``. Backup files for individual mbox files might have a ``.gz`` or ``.bz2`` extension depending on what kind of compression you specified in configuration. Backup files for mbox directories will have a ``.tar``, ``.tar.gz`` or ``.tar.bz2`` extension, again depending on what kind of compression you specified in configuration. There is one backup file for each configured mbox file or directory. The backup file name represents the name of the file or directory and the date it was backed up. So, the file ``mbox-20060624-home-user-mail-greylist`` represents the backup for ``/home/user/mail/greylist`` run on 24 Jun 2006. Likewise, ``mbox-20060624-home-user-mail.tar`` represents the backup for the ``/home/user/mail`` directory run on that same date. Once you have found the files you are looking for, the restoration procedure is fairly simple. First, concatenate all of the backup files together. Then, use grepmail to eliminate duplicate messages (if any). Here is an example for a single backed-up file: :: root:/tmp# rm restore.mbox # make sure it's not left over root:/tmp# cat mbox-20060624-home-user-mail-greylist >> restore.mbox root:/tmp# cat mbox-20060625-home-user-mail-greylist >> restore.mbox root:/tmp# cat mbox-20060626-home-user-mail-greylist >> restore.mbox root:/tmp# grepmail -a -u restore.mbox > nodups.mbox At this point, ``nodups.mbox`` contains all of the backed-up messages from ``/home/user/mail/greylist``. Of course, if your backups are compressed, you'll have to use ``zcat`` or ``bzcat`` rather than just ``cat``. If you are backing up mbox directories rather than individual files, see the filesystem instructions for notes on now to extract the individual files from inside tar archives. Extract the files you are interested in, and then concatenate them together just like shown above for the individual case. .. _cedar-recovering-split: Recovering Data split by the Split Extension -------------------------------------------- The Split extension takes large files and splits them up into smaller files. Typically, it would be used in conjunction with the ``cback3-span`` command. The split up files are not difficult to work with. Simply find all of the files --- which could be split between multiple discs --- and concatenate them together. :: root:/tmp# rm usr-src-software.tar.gz # make sure it's not there root:/tmp# cat usr-src-software.tar.gz_00001 >> usr-src-software.tar.gz root:/tmp# cat usr-src-software.tar.gz_00002 >> usr-src-software.tar.gz root:/tmp# cat usr-src-software.tar.gz_00003 >> usr-src-software.tar.gz Then, use the resulting file like usual. Remember, you need to have *all* of the files that the original large file was split into before this will work. If you are missing a file, the result of the concatenation step will be either a corrupt file or a truncated file (depending on which chunks you did not include). ---------- *Previous*: :doc:`depends` • *Next*: :doc:`securingssh` .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/docs/manual/securingssh.rst0000644000000000000000000002102614567004737016451 0ustar00.. _cedar-securingssh: Securing Password-less SSH Connections ====================================== Cedar Backup relies on password-less public key SSH connections to make various parts of its backup process work. Password-less ``scp`` is used to stage files from remote clients to the master, and password-less ``ssh`` is used to execute actions on managed clients. Normally, it is a good idea to avoid password-less SSH connections in favor of using an SSH agent. The SSH agent manages your SSH connections so that you don't need to type your passphrase over and over. You get most of the benefits of a password-less connection without the risk. Unfortunately, because Cedar Backup has to execute without human involvement (through a cron job), use of an agent really isn't feasable. We have to rely on true password-less public keys to give the master access to the client peers. Traditionally, Cedar Backup has relied on a “segmenting” strategy to minimize the risk. Although the backup typically runs as root --- so that all parts of the filesystem can be backed up --- we don't use the root user for network connections. Instead, we use a dedicated backup user on the master to initiate network connections, and dedicated users on each of the remote peers to accept network connections. With this strategy in place, an attacker with access to the backup user on the master (or even root access, really) can at best only get access to the backup user on the remote peers. We still concede a local attack vector, but at least that vector is restricted to an unprivileged user. Some Cedar Backup users may not be comfortable with this risk, and others may not be able to implement the segmentation strategy --- they simply may not have a way to create a login which is only used for backups. Fortunately, there is a solution. The SSH authorized keys file supports a way to put a “filter” in place on an SSH connection. This excerpt is from the AUTHORIZED_KEYS FILE FORMAT section of man 8 sshd: :: command="command" Specifies that the command is executed whenever this key is used for authentication. The command supplied by the user (if any) is ignored. The command is run on a pty if the client requests a pty; otherwise it is run without a tty. If an 8-bit clean channel is required, one must not request a pty or should specify no-pty. A quote may be included in the command by quoting it with a backslash. This option might be useful to restrict certain public keys to perform just a specific operation. An example might be a key that permits remote backups but nothing else. Note that the client may specify TCP and/or X11 forwarding unless they are explicitly prohibited. Note that this option applies to shell, command or subsystem execution. Essentially, this gives us a way to authenticate the commands that are being executed. We can either accept or reject commands, and we can even provide a readable error message for commands we reject. The filter is applied on the remote peer, to the key that provides the master access to the remote peer. So, let's imagine that we have two hosts: master “mickey”, and peer “minnie”. Here is the original ``~/.ssh/authorized_keys`` file for the backup user on minnie (remember, this is all on one line in the file): :: ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAxw7EnqVULBFgPcut3WYp3MsSpVB9q9iZ+awek120391k;mm0c221=3=km =m=askdalkS82mlF7SusBTcXiCk1BGsg7axZ2sclgK+FfWV1Jm0/I9yo9FtAZ9U+MmpL901231asdkl;ai1-923ma9s=9= 1-2341=-a0sd=-sa0=1z= backup@mickey This line is the public key that minnie can use to identify the backup user on mickey. Assuming that there is no passphrase on the private key back on mickey, the backup user on mickey can get direct access to minnie. To put the filter in place, we add a command option to the key, like this: :: command="/opt/backup/validate-backup" ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAxw7EnqVULBFgPcut3WYp 3MsSpVB9q9iZ+awek120391k;mm0c221=3=km=m=askdalkS82mlF7SusBTcXiCk1BGsg7axZ2sclgK+FfWV1Jm0/I9yo9F tAZ9U+MmpL901231asdkl;ai1-923ma9s=9=1-2341=-a0sd=-sa0=1z= backup@mickey Basically, the command option says that whenever this key is used to successfully initiate a connection, the ``/opt/backup/validate-backup`` command will be run *instead of* the real command that came over the SSH connection. Fortunately, the interface gives the command access to shell variables that can be used to invoke the original command if you want to. A very basic ``validate-backup`` script might look something like this: :: #!/bin/bash if [[ "${SSH_ORIGINAL_COMMAND}" == "ls -l" ]] ; then ${SSH_ORIGINAL_COMMAND} else echo "Security policy does not allow command [${SSH_ORIGINAL_COMMAND}]." exit 1 fi This script allows exactly ``ls -l`` and nothing else. If the user attempts some other command, they get a nice error message telling them that their command has been disallowed. For remote commands executed over ``ssh``, the original command is exactly what the caller attempted to invoke. For remote copies using the legacy SCP protocol, the commands are either ``scp -f file`` (copy *from* the peer to the master) or ``scp -t file`` (copy *to* the peer from the master). When using the SFTP protocol, which is the default in Debian starting with bookworm, the command is simply ``/usr/lib/openssh/sftp-server``. As a result, this mechanism is really only useful if you force the legacy SCP protocol using ``scp -O``. If you want, you can see what command SSH thinks it is executing by using ``ssh -v`` or ``scp -O -v``. The command will be right at the top, something like this: :: Executing: program /usr/bin/ssh host mickey, user (unspecified), command scp -v -f .profile OpenSSH_4.3p2 Debian-9, OpenSSL 0.9.8c 05 Sep 2006 debug1: Reading configuration data /home/backup/.ssh/config debug1: Applying options for minnie debug1: Reading configuration data /etc/ssh/ssh_config debug1: Applying options for * debug2: ssh_connect: needpriv 0 Omit the ``-v`` and you have your command: ``scp -f .profile``. For a normal, non-managed setup, you need to allow the following commands, where ``/path/to/collect/`` is replaced with the real path to the collect directory on the remote peer: :: scp -f /path/to/collect/cback.collect scp -f /path/to/collect/* scp -t /path/to/collect/cback.stage If you are configuring a managed client, then you also need to list the exact command lines that the master will be invoking on the managed client. You are guaranteed that the master will invoke one action at a time, so if you list two lines per action (full and non-full) you should be fine. Here's an example for the collect action: :: /usr/bin/cback3 --full collect /usr/bin/cback3 collect Of course, you would have to list the actual path to the ``cback3`` executable --- exactly the one listed in the ```` configuration option for your managed peer. Below is the script that I use for my own backups, to allow the master to stage files from each client. This is stored as ``~/.ssh/validate-backup`` and is referenced in ``~/.ssh/authorized-keys`` as described above. :: # Since this script is specified as the command in ~/.ssh/authorized_keys, it # acts as a "filter" and prevents the backup user from doing anything except # specific Cedar Backup actions (stage, in this case, via scp). # See the AUTHORIZED_KEYS FILE FORMAT section in sshd(8) for more information. # As of Debian bookworm, the sshd server implements SCP over SFTP. That breaks # this filter, because the SSH command is just "/usr/lib/openssh/sftp-server". # The workaround is to use `scp -O` to force the old protocol. typeset -x COLLECTDIR=/data/backup/collect typeset -x CMD1="scp -f ${COLLECTDIR}/cback.collect" # check collect indicator typeset -x CMD2="scp -f ${COLLECTDIR}/*" # stage all files typeset -x CMD3="scp -t ${COLLECTDIR}/cback.stage" # write the stage indicator if [[ "${SSH_ORIGINAL_COMMAND}" == "${CMD1}" ]] ; then ${SSH_ORIGINAL_COMMAND} elif [[ "${SSH_ORIGINAL_COMMAND}" == "${CMD2}" ]]; then ${SSH_ORIGINAL_COMMAND} elif [[ "${SSH_ORIGINAL_COMMAND}" == "${CMD3}" ]]; then ${SSH_ORIGINAL_COMMAND} else echo "Security policy does not allow command [${SSH_ORIGINAL_COMMAND}]." exit 1 fi ---------- *Previous*: :doc:`recovering` • *Next*: :doc:`copyright` .. |note| image:: images/note.png .. |tip| image:: images/tip.png .. |warning| image:: images/warning.png ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/manpages/cback3-amazons3-sync.10000644000000000000000000001633714567004737016674 0ustar00.\" vim: set ft=nroff .\" .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" # .\" # C E D A R .\" # S O L U T I O N S "Software done right." .\" # S O F T W A R E .\" # .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" # .\" # Author : Kenneth J. Pronovici .\" # Language : nroff .\" # Project : Cedar Backup, release 3 .\" # Purpose : Manpage for cback3-amazons3-sync script .\" # .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" .TH cback3\-amazons3-sync "1" "Nov 2020" "Cedar Backup 3" "Kenneth J. Pronovici" .SH NAME cback3\-amazons3-sync \- Synchronize a local directory with an Amazon S3 bucket .SH SYNOPSIS .B cback3\-amazons3\-sync [\fIswitches\fR] sourceDir s3BucketUrl .SH DESCRIPTION .PP This is the Cedar Backup 3 Amazon S3 sync tool. It synchronizes a local directory to an Amazon S3 cloud storage bucket. After the sync is complete, a validation step is taken. An error is reported if the contents of the bucket do not match the source directory, or if the indicated size for any file differs. .PP Generally, one can run the cback3\-amazons3\-sync command with no special switches. This will start it using the default Cedar Backup log file, etc. You only need to use the switches if you need to change the default behavior. .SH MIGRATING FROM VERSION 2 TO VERSION 3 .PP The main difference between Cedar Backup version 2 and Cedar Backup version 3 is the targeted Python interpreter. For most users, migration should be straightforward. See the discussion found at cback3(1) or reference the Cedar Backup user guide. .SH ARGUMENTS .TP \fBsourceDir\fR The source directory on a local disk. .TP \fBs3BucketUrl\fR The URL specifying the location of the Amazon S3 cloud storage bucket to synchronize with, like \fIs3://example.com\-backup/subdir\fR. .SH SWITCHES .TP \fB\-h\fR, \fB\-\-help\fR Display usage/help listing. .TP \fB\-V\fR, \fB\-\-version\fR Display version information. .TP \fB\-b\fR, \fB\-\-verbose\fR Print verbose output to the screen as well writing to the logfile. When this option is enabled, most information that would normally be written to the logfile will also be written to the screen. .TP \fB\-l\fR, \fB\-\-logfile\fR Specify the path to an alternate logfile. The default logfile file is \fI/var/log/cback3.log\fR. .TP \fB\-o\fR, \fB\-\-owner\fR Specify the ownership of the logfile, in the form user:group. The default ownership is \fIroot:adm\fR, to match the Debian standard for most logfiles. This value will only be used when creating a new logfile. If the logfile already exists when the cback3 script is executed, it will retain its existing ownership and mode. Only user and group names may be used, not numeric uid and gid values. .TP \fB\-m\fR, \fB\-\-mode\fR Specify the permissions for the logfile, using the numeric mode as in chmod(1). The default mode is \fI640\fR (\-rw\-r\-\-\-\-\-). This value will only be used when creating a new logfile. If the logfile already exists when the cback3 script is executed, it will retain its existing ownership and mode. .TP \fB\-O\fR, \fB\-\-output\fR Record some sub-command output to the logfile. When this option is enabled, all output from system commands will be logged. This might be useful for debugging or just for reference. .TP \fB\-d\fR, \fB\-\-debug\fR Write debugging information to the logfile. This option produces a high volume of output, and would generally only be needed when debugging a problem. This option implies the \-\-output option, as well. .TP \fB\-s\fR, \fB\-\-stack\fR Dump a Python stack trace instead of swallowing exceptions. This forces Cedar Backup to dump the entire Python stack trace associated with an error, rather than just propagating last message it received back up to the user interface. Under some circumstances, this is useful information to include along with a bug report. .TP \fB\-D\fR, \fB\-\-diagnostics\fR Display runtime diagnostic information and then exit. This diagnostic information is often useful when filing a bug report. .TP \fB\-v\fR, \fB\-\-verifyOnly\fR Only verify the S3 bucket contents against the directory on disk. Do not make any changes to the S3 bucket or transfer any files. This is intended as a quick check to see whether the sync is up-to-date. Although no files are transferred, the tool will still execute the source filename encoding check. .TP \fB\-u\fR, \fB\-\-uploadOnly\fR Implement a partial or "upload only" sync, instead of a full synchronization. Normally, synchronization would remove files that exist in S3 but do not exist in the directory on disk. With this flag, new files are uploaded, but no files are removed in S3. .TP \fB\-w\fR, \fB\-\-ignoreWarnings\fR The AWS CLI S3 sync process is very picky about filename encoding. Files that the Linux filesystem handles with no problems can cause problems in S3 if the filename cannot be encoded properly in your configured locale. As of this writing, filenames like this will cause the sync process to abort without transferring all files as expected. To avoid confusion, the tool tries to guess which files in the source directory will cause problems, and refuses to execute the AWS CLI S3 sync if any problematic files exist. If you'd rather proceed anyway, use this flag. .SH RETURN VALUES .PP This command returns 0 (zero) upon normal completion, and several other error codes related to particular errors. .TP \fB1\fR The Python interpreter version is not supported. .TP \fB2\fR Error processing command\-line arguments. .TP \fB3\fR Error configuring logging. .TP \fB5\fR Backup was interrupted with a CTRL\-C or similar. .TP \fB6\fR Other error during processing. .SH NOTES .PP This tool is a wrapper over the Amazon AWS CLI interface found in the aws(1) command. Specifically, cback3\-amazons3\-sync invokes "aws s3 sync" followed by "aws s3api list\-objects". .PP Cedar Backup itself is designed to run as root. However, cback3\-amazons3\-sync can be run safely as any user that is configured to use the Amazon AWS CLI interface. The aws(1) command will be executed by the same user which is executing cback3\-amazons3\-sync. .PP You must configure the AWS CLI interface to have a valid connection to Amazon S3 infrastructure before using cback3\-amazons3\-sync. For more information about how to accomplish this, see the Cedar Backup user guide. .SH SEE ALSO cback3(1) .SH FILES .TP \fI/var/log/cback3.log\fR - Default log file .SH URLS .TP The project homepage is: \fIhttps://github.com/pronovic/cedar\-backup3\fR .SH BUGS .PP If you find a bug, please report it. .PP If possible, give me the output from \-\-diagnostics, all of the error messages that the script printed into its log, and also any stack\-traces (exceptions) that Python printed. It would be even better if you could tell me how to reproduce the problem, for instance by sending me your configuration file. .PP Report bugs to or via GitHub issues tracker. .SH AUTHOR Written and maintained by Kenneth J. Pronovici with contributions from others. .SH COPYRIGHT Copyright (c) 2004\-2020 Kenneth J. Pronovici. .PP This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/manpages/cback3-span.10000644000000000000000000001346614567004737015130 0ustar00.\" vim: set ft=nroff .\" .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" # .\" # C E D A R .\" # S O L U T I O N S "Software done right." .\" # S O F T W A R E .\" # .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" # .\" # Author : Kenneth J. Pronovici .\" # Language : nroff .\" # Project : Cedar Backup, release 3 .\" # Purpose : Manpage for cback3-span script .\" # .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" .TH cback3\-span "1" "Aug 2019" "Cedar Backup 3" "Kenneth J. Pronovici" .SH NAME cback3\-span \- Span staged data among multiple discs .SH SYNOPSIS .B cback3\-span [\fIswitches\fR] .SH DESCRIPTION .PP This is the Cedar Backup 3 span tool. It is intended for use by people who back up more data than can fit on a single disc. It allows a user to split (span) staged data between more than one disc. It can't be a Cedar Backup extension in the usual sense because it requires user input when switching media. .PP Generally, one can run the cback3\-span command with no arguments. This will start it using the default configuration file, the default log file, etc. You only need to use the switches if you need to change the default behavior. .PP This command takes most of its configuration from the Cedar Backup configuration file, specifically the store section. Then, more information is gathered from the user interactively while the command is running. .SH MIGRATING FROM VERSION 2 TO VERSION 3 .PP The main difference between Cedar Backup version 2 and Cedar Backup version 3 is the targeted Python interpreter. For most users, migration should be straightforward. See the discussion found at cback3(1) or reference the Cedar Backup user guide. .SH SWITCHES .TP \fB\-h\fR, \fB\-\-help\fR Display usage/help listing. .TP \fB\-V\fR, \fB\-\-version\fR Display version information. .TP \fB\-b\fR, \fB\-\-verbose\fR Print verbose output to the screen as well writing to the logfile. When this option is enabled, most information that would normally be written to the logfile will also be written to the screen. .TP \fB\-c\fR, \fB\-\-config\fR Specify the path to an alternate configuration file. The default configuration file is \fI/etc/cback3.conf\fR. .TP \fB\-l\fR, \fB\-\-logfile\fR Specify the path to an alternate logfile. The default logfile file is \fI/var/log/cback3.log\fR. .TP \fB\-o\fR, \fB\-\-owner\fR Specify the ownership of the logfile, in the form user:group. The default ownership is \fIroot:adm\fR, to match the Debian standard for most logfiles. This value will only be used when creating a new logfile. If the logfile already exists when the cback3 script is executed, it will retain its existing ownership and mode. Only user and group names may be used, not numeric uid and gid values. .TP \fB\-m\fR, \fB\-\-mode\fR Specify the permissions for the logfile, using the numeric mode as in chmod(1). The default mode is \fI640\fR (\-rw\-r\-\-\-\-\-). This value will only be used when creating a new logfile. If the logfile already exists when the cback3 script is executed, it will retain its existing ownership and mode. .TP \fB\-O\fR, \fB\-\-output\fR Record some sub-command output to the logfile. When this option is enabled, all output from system commands will be logged. This might be useful for debugging or just for reference. .TP \fB\-d\fR, \fB\-\-debug\fR Write debugging information to the logfile. This option produces a high volume of output, and would generally only be needed when debugging a problem. This option implies the \-\-output option, as well. .TP \fB\-s\fR, \fB\-\-stack\fR Dump a Python stack trace instead of swallowing exceptions. This forces Cedar Backup to dump the entire Python stack trace associated with an error, rather than just propagating last message it received back up to the user interface. Under some circumstances, this is useful information to include along with a bug report. .TP \fB\-D\fR, \fB\-\-diagnostics\fR Display runtime diagnostic information and then exit. This diagnostic information is often useful when filing a bug report. .SH RETURN VALUES .PP This command returns 0 (zero) upon normal completion, and six other error codes related to particular errors. .TP \fB1\fR The Python interpreter version is not supported. .TP \fB2\fR Error processing command\-line arguments. .TP \fB3\fR Error configuring logging. .TP \fB4\fR Error parsing indicated configuration file. .TP \fB5\fR Backup was interrupted with a CTRL\-C or similar. .TP \fB6\fR Other error during processing. .SH NOTES .PP Cedar Backup itself is designed to run as root, since otherwise it's difficult to back up system directories or write the CD or DVD device. However, cback3\-span can be run safely as any user that has read access to the Cedar Backup staging directories and write access to the CD or DVD device. .SH SEE ALSO cback3(1) .SH FILES .TP \fI/etc/cback3.conf\fR - Default configuration file .TP \fI/var/log/cback3.log\fR - Default log file .SH URLS .TP The project homepage is: \fIhttps://github.com/pronovic/cedar\-backup3\fR .SH BUGS .PP If you find a bug, please report it. .PP If possible, give me the output from \-\-diagnostics, all of the error messages that the script printed into its log, and also any stack\-traces (exceptions) that Python printed. It would be even better if you could tell me how to reproduce the problem, for instance by sending me your configuration file. .PP Report bugs to or via GitHub issues tracker. .SH AUTHOR Written and maintained by Kenneth J. Pronovici with contributions from others. .SH COPYRIGHT Copyright (c) 2004\-2020 Kenneth J. Pronovici. .PP This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/manpages/cback3.10000644000000000000000000002732314567004737014166 0ustar00.\" vim: set ft=nroff .\" .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" # .\" # C E D A R .\" # S O L U T I O N S "Software done right." .\" # S O F T W A R E .\" # .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" # .\" # Author : Kenneth J. Pronovici .\" # Language : nroff .\" # Project : Cedar Backup, release 3 .\" # Purpose : Manpage for cback3 script .\" # .\" # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # .\" .TH cback3 "1" "Aug 2019" "Cedar Backup 3" "Kenneth J. Pronovici" .SH NAME cback3 \- Local and remote backups to CD or DVD media or Amazon S3 storage .SH SYNOPSIS .B cback3 [\fIswitches\fR] action(s) .SH DESCRIPTION .PP The cback3 script provides the command\-line interface for Cedar Backup 3. Cedar Backup 3 is a software package designed to manage system backups for a pool of local and remote machines. It understands how to back up filesystem data as well as MySQL and PostgreSQL databases and Subversion repositories. It can also be easily extended to support other kinds of data sources. .PP Cedar Backup 3 is focused around weekly backups to a single CD or DVD disc, with the expectation that the disc will be changed or overwritten at the beginning of each week. If your hardware is new enough, Cedar Backup can write multisession discs, allowing you to add incremental data to a disc on a daily basis. .PP Alternately, Cedar Backup 3 can write your backups to the Amazon S3 cloud rather than relying on physical media. .SH BACKUP CONCEPTS .PP There are two kinds of machines in a Cedar Backup pool. One machine (the \fImaster\fR) has a CD or DVD writer on it and is where the backup is written to disc. The others (\fIclients\fR) collect data to be written to disc by the master. Collectively, the master and client machines in a pool are all referred to as \fIpeer\fR machines. There are four actions that take place as part of the backup process: \fIcollect\fR, \fIstage\fR, \fIstore\fR and \fIpurge\fR. Both the master and the clients execute the collect and purge actions, but only the master executes the stage and store actions. The configuration file \fI/etc/cback3.conf\fR controls the actions taken during the collect, stage, store and purge actions. .PP Cedar Backup also supports the concept of \fImanaged clients\fR. Managed clients have their entire backup process managed by the master via a remote shell. The same actions are run as part of the backup process, but the master controls when the actions are executed on the clients rather than the clients controlling it for themselves. This facility is intended for use in environments where a scheduler like cron is not available. .SH MIGRATING FROM VERSION 2 TO VERSION 3 .PP The main difference between Cedar Backup version 2 and Cedar Backup version 3 is the targeted Python interpreter. Cedar Backup version 2 was designed for Python 2, while version 3 is a conversion of the original code to Python 3. Other than that, both versions are functionally equivalent. The configuration format is unchanged, and you can mix\-and\-match masters and clients of different versions in the same backup pool. Both versions will be fully supported until around the time of the Python 2 end\-of\-life in 2020, but you should plan to migrate sooner than that if possible. .PP A major design goal for version 3 was to facilitate easy migration testing for users, by making it possible to install version 3 on the same server where version 2 was already in use. A side effect of this design choice is that all of the executables, configuration files, and logs changed names in version 3. Where version 2 used \fIcback\fR, version 3 uses \fIcback3\fR: \fIcback3.conf\fR instead of \fIcback.conf\fR, \fIcback3.log\fR instead of \fIcback.log\fR, etc. .PP So, while migrating from version 2 to version 3 is relatively straightforward, you will have to make some changes manually. You will need to create a new configuration file (or soft link to the old one), modify your cron jobs to use the new executable name, etc. You can migrate one server at a time in your pool with no ill effects, or even incrementally migrate a single server by using version 2 and version 3 on different days of the week or for different parts of the backup. .SH SWITCHES .TP \fB\-h\fR, \fB\-\-help\fR Display usage/help listing. .TP \fB\-V\fR, \fB\-\-version\fR Display version information. .TP \fB\-b\fR, \fB\-\-verbose\fR Print verbose output to the screen as well writing to the logfile. When this option is enabled, most information that would normally be written to the logfile will also be written to the screen. .TP \fB\-q\fR, \fB\-\-quiet\fR Run quietly (display no output to the screen). .TP \fB\-c\fR, \fB\-\-config\fR Specify the path to an alternate configuration file. The default configuration file is \fI/etc/cback3.conf\fR. .TP \fB\-f\fR, \fB\-\-full\fR Perform a full backup, regardless of configuration. For the collect action, this means that any existing information related to incremental backups will be ignored and rewritten; for the store action, this means that a new disc will be started. .TP \fB\-M\fR, \fB\-\-managed\fR Include managed clients when executing actions. If the action being executed is listed as a managed action for a managed client, execute the action on that client after executing the action locally. .TP \fB\-N\fR, \fB\-\-managed-only\fR Include only managed clients when executing actions. If the action being executed is listed as a managed action for a managed client, execute the action on that client, but do not execute the action locally. .TP \fB\-l\fR, \fB\-\-logfile\fR Specify the path to an alternate logfile. The default logfile file is \fI/var/log/cback3.log\fR. .TP \fB\-o\fR, \fB\-\-owner\fR Specify the ownership of the logfile, in the form user:group. The default ownership is \fIroot:adm\fR, to match the Debian standard for most logfiles. This value will only be used when creating a new logfile. If the logfile already exists when the cback3 script is executed, it will retain its existing ownership and mode. Only user and group names may be used, not numeric uid and gid values. .TP \fB\-m\fR, \fB\-\-mode\fR Specify the permissions for the logfile, using the numeric mode as in chmod(1). The default mode is \fI640\fR (\-rw\-r\-\-\-\-\-). This value will only be used when creating a new logfile. If the logfile already exists when the cback3 script is executed, it will retain its existing ownership and mode. .TP \fB\-O\fR, \fB\-\-output\fR Record some sub-command output to the logfile. When this option is enabled, all output from system commands will be logged. This might be useful for debugging or just for reference. .TP \fB\-d\fR, \fB\-\-debug\fR Write debugging information to the logfile. This option produces a high volume of output, and would generally only be needed when debugging a problem. This option implies the \-\-output option, as well. .TP \fB\-s\fR, \fB\-\-stack\fR Dump a Python stack trace instead of swallowing exceptions. This forces Cedar Backup to dump the entire Python stack trace associated with an error, rather than just propagating last message it received back up to the user interface. Under some circumstances, this is useful information to include along with a bug report. .TP \fB\-D\fR, \fB\-\-diagnostics\fR Display runtime diagnostic information and then exit. This diagnostic information is often useful when filing a bug report. .SH ACTIONS .TP \fBall\fR Take all normal actions (collect, stage, store, purge), in that order. .TP \fBcollect\fR Take the collect action, creating tarfiles for each directory specified in the collect section of the configuration file. .TP \fBstage\fR Take the stage action, copying tarfiles from each peer in the backup pool to the daily staging directory, based on the stage section of the configuration file. .TP \fBstore\fR Take the store action, writing the daily staging directory to disc based on the store section of the configuration file. .TP \fBpurge\fR Take the purge action, removing old and outdated files as specified in the purge section of the configuration file. .TP \fBrebuild\fR The rebuild action attempts to rebuild "this week's" disc from any remaining unpurged staging directories. Typically, it is used to make a copy of a backup, replace lost or damaged media, or to switch to new media mid-week for some other reason. .TP \fBvalidate\fR Ensure that configuration is valid, but take no other action. Validation checks that the configuration file can be found and can be parsed, and also checks for typical configuration problems, such as directories that are not writable or problems with the target SCSI device. .SH RETURN VALUES .PP Cedar Backup returns 0 (zero) upon normal completion, and six other error codes related to particular errors. .TP \fB1\fR The Python interpreter version is not supported. .TP \fB2\fR Error processing command\-line arguments. .TP \fB3\fR Error configuring logging. .TP \fB4\fR Error parsing indicated configuration file. .TP \fB5\fR Backup was interrupted with a CTRL\-C or similar. .TP \fB6\fR Error executing specified backup actions. .SH NOTES .PP The script is designed to run as root, since otherwise it's difficult to back up system directories or write the CD or DVD device. However, pains are taken to switch to a backup user (specified in configuration) when appropriate. .PP To use the script, you must specify at least one action to take. More than one of the "collect", "stage", "store" or "purge" actions may be specified, in any arbitrary order. The "all", "rebuild" or "validate" actions may not be combined with other actions. If more than one action is specified, then actions will be taken in a sensible order (generally collect, followed by stage, followed by store, followed by purge). .PP If you have configured any Cedar Backup extensions, then the actions associated with those extensions may also be specified on the command line. If you specify any other actions along with an extended action, the actions will be executed in a sensible order per configuration. However, the "all" action never executes extended actions. .PP Note that there is no facility for restoring backups. It is assumed that the user can deal with copying tarfiles off disc and using them to restore missing files as needed. The user manual provides detailed instructions in Appendix C. .PP Finally, you should be aware that backups to CD or DVD can probably be read by any user which has permissions to mount the CD or DVD drive. If you intend to leave the backup disc in the drive at all times, you may want to consider this when setting up device permissions on your machine. You might also want to investigate the encrypt extension. .SH FILES .TP \fI/etc/cback3.conf\fR - Default configuration file .TP \fI/var/log/cback3.log\fR - Default log file .SH URLS .TP The project homepage is: \fIhttps://github.com/pronovic/cedar\-backup3\fR .SH BUGS .PP There probably are bugs in this code. However, it is in active use for my own backups, and I fix problems as I notice them. If you find a bug, please report it. .PP If possible, give me the output from \-\-diagnostics, all of the error messages that the script printed into its log, and also any stack\-traces (exceptions) that Python printed. It would be even better if you could tell me how to reproduce the problem, for instance by sending me your configuration file. .PP Report bugs to or via GitHub issues tracker. .SH AUTHOR Written and maintained by Kenneth J. Pronovici with contributions from others. .SH COPYRIGHT Copyright (c) 2004\-2020 Kenneth J. Pronovici. .PP This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919281.3092072 cedar_backup3-3.8.1/pyproject.toml0000644000000000000000000000622614567004761014073 0ustar00[tool.poetry] name = "cedar-backup3" version = "3.8.1" # published version is managed using Git tags (see below) description = "Implements local and remote backups to CD/DVD and Amazon S3" keywords = [ 'local', 'remote', 'backup', 'scp' ] authors = ["Kenneth J. Pronovici "] license = "GPL-2.0-only" readme = "PyPI.md" homepage = "https://pypi.org/project/cedar-backup3/" repository = "https://github.com/pronovic/cedar-backup3" include = [ { path = 'Changelog', format = 'sdist' }, { path = 'NOTICE', format = 'sdist' }, { path = 'LICENSE', format = 'sdist' }, { path = 'README.md', format = 'sdist' }, { path = 'docs', format = 'sdist' }, { path = 'manpages', format = 'sdist' }, { path = 'samples', format = 'sdist' }, { path = 'tests', format = 'sdist' }, ] packages = [ { include = "CedarBackup3", from = "src" } ] classifiers=[ "Programming Language :: Python :: 3", "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: GNU General Public License v2 (GPLv2)", "Operating System :: OS Independent", "Environment :: Console", "Intended Audience :: System Administrators", "Natural Language :: English", "Topic :: Software Development :: Libraries", "Topic :: System :: Archiving :: Backup", "Topic :: Utilities", ] # Published version is managed using Git tags # We get either the tag (like "0.24.1") or a snapshot-type version (like "0.24.1+3.e8319c4") # If the plugin is not installed, then the version is always "0.0.0", taken from above [tool.poetry-dynamic-versioning] enable = false pattern = '^[vV](?P\d+\.\d+\.\d+)' # this extracts the version from our vX.Y.Z tag format format-jinja = "{% if distance == 0 and not dirty %}{{ base }}{% else %}{{ base }}+{{ distance }}.{{ commit }}{% endif %}" [tool.poetry.scripts] cback3 = 'CedarBackup3.scripts:cback3' cback3-amazons3-sync = 'CedarBackup3.scripts:amazons3' cback3-span = 'CedarBackup3.scripts:span' [tool.poetry.dependencies] python = ">=3.9,<4" chardet = "^5.1.0,<6" # Debian bookworm only has 5.1.0 importlib-metadata = "^4.12.0,<5" # Debian bookworm only has 4.12.0 sphinx = { version="^5.3.0,<6", optional=true } # Debian bookworm only has 5.3.0 sphinx-autoapi = { version="^2.0.0,<3", optional=true } # Debian bookworm only has 2.0.0 astroid = "<3" # Newer versions conflict with older Sphinx [tool.poetry.extras] docs = [ "sphinx", "sphinx-autoapi" ] [tool.poetry.group.dev.dependencies] coverage = "^6.5.0" # coveralls needs v6 pylint = "^2.17.7,<3" # newer versions need astroid 3 pre-commit = "^3.4.0" black = "^24.2.0" isort = "^5.12.0" coveralls = "^3.3.1" colorama = "~0, >=0.4.6" [tool.black] line-length = 132 target-version = [ 'py39', 'py310', 'py311', 'py312' ] include = '\.pyi?$' exclude = ''' /( \.git | __pycache__ | \.tox | \.venv | \.poetry | build | dist | docs | notes )/ ''' [tool.isort] profile = "black" line_length = 132 skip_glob = [ "docs", "notes" ] [build-system] requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"] build-backend = "poetry_dynamic_versioning.backend" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.140956 cedar_backup3-3.8.1/samples/cback3.conf.sample0000644000000000000000000001100014567004737016065 0ustar00 Kenneth J. Pronovici 1.3 Sample sysinfo CedarBackup3.extend.sysinfo executeAction 95 mysql CedarBackup3.extend.mysql executeAction 96 postgresql CedarBackup3.extend.postgresql executeAction 97 subversion CedarBackup3.extend.subversion executeAction 98 mbox CedarBackup3.extend.mbox executeAction 99 encrypt CedarBackup3.extend.encrypt executeAction 299 tuesday /opt/backup/tmp backup group /usr/bin/scp -B cdrecord /opt/local/bin/cdrecord mkisofs /opt/local/bin/mkisofs collect echo "I AM A PRE-ACTION HOOK RELATED TO COLLECT" collect echo "I AM A POST-ACTION HOOK RELATED TO COLLECT" /opt/backup/collect daily targz .cbignore /etc incr /home/root/.profile weekly /opt/backup/stage debian local /opt/backup/collect /opt/backup/stage cdrw-74 cdwriter /dev/cdrw 0,0,0 4 Y N weekly 5.1 /opt/backup/stage 7 /opt/backup/collect 0 mlogin bzip2 Y plogin bzip2 N db1 db2 incr bzip2 FSFS /opt/svn/repo1 BDB /opt/svn/repo2 incr bzip2 /home/user1/mail/greylist daily /home/user2/mail gzip gpg Backup User ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/__init__.py0000644000000000000000000000454114567004737016307 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides package initialization # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements local and remote backups to CD or DVD media. Cedar Backup is a software package designed to manage system backups for a pool of local and remote machines. Cedar Backup understands how to back up filesystem data as well as MySQL and PostgreSQL databases and Subversion repositories. It can also be easily extended to support other kinds of data sources. Cedar Backup is focused around weekly backups to a single CD or DVD disc, with the expectation that the disc will be changed or overwritten at the beginning of each week. If your hardware is new enough, Cedar Backup can write multisession discs, allowing you to add incremental data to a disc on a daily basis. Besides offering command-line utilities to manage the backup process, Cedar Backup provides a well-organized library of backup-related functionality, written in the Python programming language. :author: Kenneth J. Pronovici """ ######################################################################## # Package initialization ######################################################################## # Using 'from CedarBackup3 import *' will just import the modules listed # in the __all__ variable. import CedarBackup3.actions import CedarBackup3.cli import CedarBackup3.config import CedarBackup3.extend import CedarBackup3.filesystem import CedarBackup3.knapsack import CedarBackup3.peer import CedarBackup3.release import CedarBackup3.tools import CedarBackup3.util import CedarBackup3.writers __all__ = [ "actions", "cli", "config", "extend", "filesystem", "knapsack", "peer", "release", "tools", "util", "writers", ] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/action.py0000644000000000000000000000317514567004737016027 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides implementation of various backup-related actions. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides interface backwards compatibility. In Cedar Backup 2.10.0, a refactoring effort took place to reorganize the code for the standard actions. The code formerly in action.py was split into various other files in the CedarBackup3.actions package. This mostly-empty file remains to preserve the Cedar Backup library interface. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## # pylint: disable=W0611 from CedarBackup3.actions.collect import executeCollect from CedarBackup3.actions.purge import executePurge from CedarBackup3.actions.rebuild import executeRebuild from CedarBackup3.actions.stage import executeStage from CedarBackup3.actions.store import executeStore from CedarBackup3.actions.validate import executeValidate ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/__init__.py0000644000000000000000000000374114567004737017750 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides package initialization # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Cedar Backup actions. This package code related to the offical Cedar Backup actions (collect, stage, store, purge, rebuild, and validate). The action modules consist of mostly "glue" code that uses other lower-level functionality to actually implement a backup. There is one module for each high-level backup action, plus a module that provides shared constants. All of the public action function implement the Cedar Backup Extension Architecture Interface, i.e. the same interface that extensions implement. :author: Kenneth J. Pronovici """ ######################################################################## # Package initialization ######################################################################## # Using 'from CedarBackup3.actions import *' will just import the modules listed # in the __all__ variable. import CedarBackup3.actions.collect import CedarBackup3.actions.constants import CedarBackup3.actions.initialize import CedarBackup3.actions.purge import CedarBackup3.actions.rebuild import CedarBackup3.actions.stage import CedarBackup3.actions.store import CedarBackup3.actions.util import CedarBackup3.actions.validate __all__ = ["constants", "collect", "initialize", "stage", "store", "purge", "util", "rebuild", "validate"] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/collect.py0000644000000000000000000005530314567004737017637 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2011,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements the standard 'collect' action. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements the standard 'collect' action. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import pickle from CedarBackup3.actions.constants import COLLECT_INDICATOR, DIGEST_EXTENSION from CedarBackup3.actions.util import writeIndicatorFile from CedarBackup3.filesystem import BackupFileList, FilesystemList from CedarBackup3.util import buildNormalizedPath, changeOwnership, displayBytes, isStartOfWeek, pathJoin ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.actions.collect") ######################################################################## # Public functions ######################################################################## ############################ # executeCollect() function ############################ # pylint: disable=W0613 def executeCollect(configPath, options, config): """ Executes the collect backup action. *Note:* When the collect action is complete, we will write a collect indicator to the collect directory, so it's obvious that the collect action has completed. The stage process uses this indicator to decide whether a peer is ready to be staged. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions TarError: If there is a problem creating a tar file """ logger.debug("Executing the 'collect' action.") if config.options is None or config.collect is None: raise ValueError("Collect configuration is not properly filled in.") if (config.collect.collectFiles is None or len(config.collect.collectFiles) < 1) and ( config.collect.collectDirs is None or len(config.collect.collectDirs) < 1 ): raise ValueError("There must be at least one collect file or collect directory.") fullBackup = options.full logger.debug("Full backup flag is [%s]", fullBackup) todayIsStart = isStartOfWeek(config.options.startingDay) resetDigest = fullBackup or todayIsStart logger.debug("Reset digest flag is [%s]", resetDigest) if config.collect.collectFiles is not None: for collectFile in config.collect.collectFiles: logger.debug("Working with collect file [%s]", collectFile.absolutePath) collectMode = _getCollectMode(config, collectFile) archiveMode = _getArchiveMode(config, collectFile) digestPath = _getDigestPath(config, collectFile.absolutePath) tarfilePath = _getTarfilePath(config, collectFile.absolutePath, archiveMode) if fullBackup or (collectMode in ["daily", "incr"]) or (collectMode == "weekly" and todayIsStart): logger.debug("File meets criteria to be backed up today.") _collectFile(config, collectFile.absolutePath, tarfilePath, collectMode, archiveMode, resetDigest, digestPath) else: logger.debug("File will not be backed up, per collect mode.") logger.info("Completed collecting file [%s]", collectFile.absolutePath) if config.collect.collectDirs is not None: for collectDir in config.collect.collectDirs: logger.debug("Working with collect directory [%s]", collectDir.absolutePath) collectMode = _getCollectMode(config, collectDir) archiveMode = _getArchiveMode(config, collectDir) ignoreFile = _getIgnoreFile(config, collectDir) linkDepth = _getLinkDepth(collectDir) dereference = _getDereference(collectDir) recursionLevel = _getRecursionLevel(collectDir) (excludePaths, excludePatterns) = _getExclusions(config, collectDir) if fullBackup or (collectMode in ["daily", "incr"]) or (collectMode == "weekly" and todayIsStart): logger.debug("Directory meets criteria to be backed up today.") _collectDirectory( config, collectDir.absolutePath, collectMode, archiveMode, ignoreFile, linkDepth, dereference, resetDigest, excludePaths, excludePatterns, recursionLevel, ) else: logger.debug("Directory will not be backed up, per collect mode.") logger.info("Completed collecting directory [%s]", collectDir.absolutePath) writeIndicatorFile(config.collect.targetDir, COLLECT_INDICATOR, config.options.backupUser, config.options.backupGroup) logger.info("Executed the 'collect' action successfully.") ######################################################################## # Private utility functions ######################################################################## ########################## # _collectFile() function ########################## def _collectFile(config, absolutePath, tarfilePath, collectMode, archiveMode, resetDigest, digestPath): """ Collects a configured collect file. The indicated collect file is collected into the indicated tarfile. For files that are collected incrementally, we'll use the indicated digest path and pay attention to the reset digest flag (basically, the reset digest flag ignores any existing digest, but a new digest is always rewritten). The caller must decide what the collect and archive modes are, since they can be on both the collect configuration and the collect file itself. Args: config: Config object absolutePath: Absolute path of file to collect tarfilePath: Path to tarfile that should be created collectMode: Collect mode to use archiveMode: Archive mode to use resetDigest: Reset digest flag digestPath: Path to digest file on disk, if needed """ backupList = BackupFileList() backupList.addFile(absolutePath) _executeBackup(config, backupList, absolutePath, tarfilePath, collectMode, archiveMode, resetDigest, digestPath) ############################### # _collectDirectory() function ############################### def _collectDirectory( config, absolutePath, collectMode, archiveMode, ignoreFile, linkDepth, dereference, resetDigest, excludePaths, excludePatterns, recursionLevel, ): """ Collects a configured collect directory. The indicated collect directory is collected into the indicated tarfile. For directories that are collected incrementally, we'll use the indicated digest path and pay attention to the reset digest flag (basically, the reset digest flag ignores any existing digest, but a new digest is always rewritten). The caller must decide what the collect and archive modes are, since they can be on both the collect configuration and the collect directory itself. Args: config: Config object absolutePath: Absolute path of directory to collect collectMode: Collect mode to use archiveMode: Archive mode to use ignoreFile: Ignore file to use linkDepth: Link depth value to use dereference: Dereference flag to use resetDigest: Reset digest flag excludePaths: List of absolute paths to exclude excludePatterns: List of patterns to exclude recursionLevel: Recursion level (zero for no recursion) """ if recursionLevel == 0: # Collect the actual directory because we're at recursion level 0 logger.info("Collecting directory [%s]", absolutePath) tarfilePath = _getTarfilePath(config, absolutePath, archiveMode) digestPath = _getDigestPath(config, absolutePath) backupList = BackupFileList() backupList.ignoreFile = ignoreFile backupList.excludePaths = excludePaths backupList.excludePatterns = excludePatterns backupList.addDirContents(absolutePath, linkDepth=linkDepth, dereference=dereference) _executeBackup(config, backupList, absolutePath, tarfilePath, collectMode, archiveMode, resetDigest, digestPath) else: # Find all of the immediate subdirectories subdirs = FilesystemList() subdirs.excludeFiles = True subdirs.excludeLinks = True subdirs.excludePaths = excludePaths subdirs.excludePatterns = excludePatterns subdirs.addDirContents(path=absolutePath, recursive=False, addSelf=False) # Back up the subdirectories separately for subdir in subdirs: _collectDirectory( config, subdir, collectMode, archiveMode, ignoreFile, linkDepth, dereference, resetDigest, excludePaths, excludePatterns, recursionLevel - 1, ) excludePaths.append(subdir) # this directory is already backed up, so exclude it # Back up everything that hasn't previously been backed up _collectDirectory( config, absolutePath, collectMode, archiveMode, ignoreFile, linkDepth, dereference, resetDigest, excludePaths, excludePatterns, 0, ) ############################ # _executeBackup() function ############################ def _executeBackup(config, backupList, absolutePath, tarfilePath, collectMode, archiveMode, resetDigest, digestPath): """ Execute the backup process for the indicated backup list. This function exists mainly to consolidate functionality between the :any:`_collectFile` and :any:`_collectDirectory` functions. Those functions build the backup list; this function causes the backup to execute properly and also manages usage of the digest file on disk as explained in their comments. For collect files, the digest file will always just contain the single file that is being backed up. This might little wasteful in terms of the number of files that we keep around, but it's consistent and easy to understand. Args: config: Config object backupList: List to execute backup for absolutePath: Absolute path of directory or file to collect tarfilePath: Path to tarfile that should be created collectMode: Collect mode to use archiveMode: Archive mode to use resetDigest: Reset digest flag digestPath: Path to digest file on disk, if needed """ if collectMode != "incr": logger.debug("Collect mode is [%s]; no digest will be used.", collectMode) if len(backupList) == 1 and backupList[0] == absolutePath: # special case for individual file logger.info("Backing up file [%s] (%s).", absolutePath, displayBytes(backupList.totalSize())) else: logger.info("Backing up %d files in [%s] (%s).", len(backupList), absolutePath, displayBytes(backupList.totalSize())) if len(backupList) > 0: backupList.generateTarfile(tarfilePath, archiveMode, True) changeOwnership(tarfilePath, config.options.backupUser, config.options.backupGroup) else: if resetDigest: logger.debug("Based on resetDigest flag, digest will be cleared.") oldDigest = {} else: logger.debug("Based on resetDigest flag, digest will loaded from disk.") oldDigest = _loadDigest(digestPath) (removed, newDigest) = backupList.removeUnchanged(oldDigest, captureDigest=True) logger.debug("Removed %d unchanged files based on digest values.", removed) if len(backupList) == 1 and backupList[0] == absolutePath: # special case for individual file logger.info("Backing up file [%s] (%s).", absolutePath, displayBytes(backupList.totalSize())) else: logger.info("Backing up %d files in [%s] (%s).", len(backupList), absolutePath, displayBytes(backupList.totalSize())) if len(backupList) > 0: backupList.generateTarfile(tarfilePath, archiveMode, True) changeOwnership(tarfilePath, config.options.backupUser, config.options.backupGroup) _writeDigest(config, newDigest, digestPath) ######################### # _loadDigest() function ######################### def _loadDigest(digestPath): """ Loads the indicated digest path from disk into a dictionary. If we can't load the digest successfully (either because it doesn't exist or for some other reason), then an empty dictionary will be returned - but the condition will be logged. Args: digestPath: Path to the digest file on disk Returns: Dictionary representing contents of digest path """ if not os.path.isfile(digestPath): digest = {} logger.debug("Digest [%s] does not exist on disk.", digestPath) else: try: with open(digestPath, "rb") as f: digest = pickle.load(f, fix_imports=True) # be compatible with Python 2 logger.debug("Loaded digest [%s] from disk: %d entries.", digestPath, len(digest)) except Exception as e: digest = {} logger.error("Failed loading digest [%s] from disk: %s", digestPath, e) return digest ########################## # _writeDigest() function ########################## def _writeDigest(config, digest, digestPath): """ Writes the digest dictionary to the indicated digest path on disk. If we can't write the digest successfully for any reason, we'll log the condition but won't throw an exception. Args: config: Config object digest: Digest dictionary to write to disk digestPath: Path to the digest file on disk """ try: with open(digestPath, "wb") as f: pickle.dump(digest, f, 0, fix_imports=True) # be compatible with Python 2 changeOwnership(digestPath, config.options.backupUser, config.options.backupGroup) logger.debug("Wrote new digest [%s] to disk: %d entries.", digestPath, len(digest)) except Exception as e: logger.error("Failed to write digest [%s] to disk: %s", digestPath, e) ######################################################################## # Private attribute "getter" functions ######################################################################## ############################ # getCollectMode() function ############################ def _getCollectMode(config, item): """ Gets the collect mode that should be used for a collect directory or file. If possible, use the one on the file or directory, otherwise take from collect section. Args: config: Config object item: ``CollectFile`` or ``CollectDir`` object Returns: Collect mode to use """ if item.collectMode is None: collectMode = config.collect.collectMode else: collectMode = item.collectMode logger.debug("Collect mode is [%s]", collectMode) return collectMode ############################# # _getArchiveMode() function ############################# def _getArchiveMode(config, item): """ Gets the archive mode that should be used for a collect directory or file. If possible, use the one on the file or directory, otherwise take from collect section. Args: config: Config object item: ``CollectFile`` or ``CollectDir`` object Returns: Archive mode to use """ if item.archiveMode is None: archiveMode = config.collect.archiveMode else: archiveMode = item.archiveMode logger.debug("Archive mode is [%s]", archiveMode) return archiveMode ############################ # _getIgnoreFile() function ############################ def _getIgnoreFile(config, item): """ Gets the ignore file that should be used for a collect directory or file. If possible, use the one on the file or directory, otherwise take from collect section. Args: config: Config object item: ``CollectFile`` or ``CollectDir`` object Returns: Ignore file to use """ if item.ignoreFile is None: ignoreFile = config.collect.ignoreFile else: ignoreFile = item.ignoreFile logger.debug("Ignore file is [%s]", ignoreFile) return ignoreFile ############################ # _getLinkDepth() function ############################ def _getLinkDepth(item): """ Gets the link depth that should be used for a collect directory. If possible, use the one on the directory, otherwise set a value of 0 (zero). Args: item: ``CollectDir`` object Returns: Link depth to use """ if item.linkDepth is None: linkDepth = 0 else: linkDepth = item.linkDepth logger.debug("Link depth is [%d]", linkDepth) return linkDepth ############################ # _getDereference() function ############################ def _getDereference(item): """ Gets the dereference flag that should be used for a collect directory. If possible, use the one on the directory, otherwise set a value of False. Args: item: ``CollectDir`` object Returns: Dereference flag to use """ if item.dereference is None: dereference = False else: dereference = item.dereference logger.debug("Dereference flag is [%s]", dereference) return dereference ################################ # _getRecursionLevel() function ################################ def _getRecursionLevel(item): """ Gets the recursion level that should be used for a collect directory. If possible, use the one on the directory, otherwise set a value of 0 (zero). Args: item: ``CollectDir`` object Returns: Recursion level to use """ if item.recursionLevel is None: recursionLevel = 0 else: recursionLevel = item.recursionLevel logger.debug("Recursion level is [%d]", recursionLevel) return recursionLevel ############################ # _getDigestPath() function ############################ def _getDigestPath(config, absolutePath): """ Gets the digest path associated with a collect directory or file. Args: config: Config object absolutePath: Absolute path to generate digest for Returns: Absolute path to the digest associated with the collect directory or file """ normalized = buildNormalizedPath(absolutePath) filename = "%s.%s" % (normalized, DIGEST_EXTENSION) digestPath = pathJoin(config.options.workingDir, filename) logger.debug("Digest path is [%s]", digestPath) return digestPath ############################# # _getTarfilePath() function ############################# def _getTarfilePath(config, absolutePath, archiveMode): """ Gets the tarfile path (including correct extension) associated with a collect directory. Args: config: Config object absolutePath: Absolute path to generate tarfile for archiveMode: Archive mode to use for this tarfile Returns: Absolute path to the tarfile associated with the collect directory """ if archiveMode == "tar": extension = "tar" elif archiveMode == "targz": extension = "tar.gz" elif archiveMode == "tarbz2": extension = "tar.bz2" normalized = buildNormalizedPath(absolutePath) filename = "%s.%s" % (normalized, extension) tarfilePath = pathJoin(config.collect.targetDir, filename) logger.debug("Tarfile path is [%s]", tarfilePath) return tarfilePath ############################ # _getExclusions() function ############################ def _getExclusions(config, collectDir): """ Gets exclusions (file and patterns) associated with a collect directory. The returned files value is a list of absolute paths to be excluded from the backup for a given directory. It is derived from the collect configuration absolute exclude paths and the collect directory's absolute and relative exclude paths. The returned patterns value is a list of patterns to be excluded from the backup for a given directory. It is derived from the list of patterns from the collect configuration and from the collect directory itself. Args: config: Config object collectDir: Collect directory object Returns: Tuple (files, patterns) indicating what to exclude """ paths = [] if config.collect.absoluteExcludePaths is not None: paths.extend(config.collect.absoluteExcludePaths) if collectDir.absoluteExcludePaths is not None: paths.extend(collectDir.absoluteExcludePaths) if collectDir.relativeExcludePaths is not None: for relativePath in collectDir.relativeExcludePaths: paths.append(pathJoin(collectDir.absolutePath, relativePath)) patterns = [] if config.collect.excludePatterns is not None: patterns.extend(config.collect.excludePatterns) if collectDir.excludePatterns is not None: patterns.extend(collectDir.excludePatterns) logger.debug("Exclude paths: %s", paths) logger.debug("Exclude patterns: %s", patterns) return (paths, patterns) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/constants.py0000644000000000000000000000231614567004737020222 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides common constants used by standard actions. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides common constants used by standard actions. :author: Kenneth J. Pronovici """ ######################################################################## # Module-wide constants and variables ######################################################################## DIR_TIME_FORMAT = "%Y/%m/%d" DIGEST_EXTENSION = "sha" INDICATOR_PATTERN = [r"cback\..*"] COLLECT_INDICATOR = "cback.collect" STAGE_INDICATOR = "cback.stage" STORE_INDICATOR = "cback.store" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/initialize.py0000644000000000000000000000604514567004737020352 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements the standard 'initialize' action. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements the standard 'initialize' action. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging from CedarBackup3.actions.util import initializeMediaState ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.actions.initialize") ######################################################################## # Public functions ######################################################################## ############################### # executeInitialize() function ############################### # pylint: disable=W0613 def executeInitialize(configPath, options, config): """ Executes the initialize action. The initialize action initializes the media currently in the writer device so that Cedar Backup can recognize it later. This is an optional step; it's only required if checkMedia is set on the store configuration. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration """ logger.debug("Executing the 'initialize' action.") if config.options is None or config.store is None: raise ValueError("Store configuration is not properly filled in.") initializeMediaState(config) logger.info("Executed the 'initialize' action successfully.") ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/purge.py0000644000000000000000000000665514567004737017342 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements the standard 'purge' action. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements the standard 'purge' action. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging from CedarBackup3.filesystem import PurgeItemList ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.actions.purge") ######################################################################## # Public functions ######################################################################## ########################## # executePurge() function ########################## # pylint: disable=W0613 def executePurge(configPath, options, config): """ Executes the purge backup action. For each configured directory, we create a purge item list, remove from the list anything that's younger than the configured retain days value, and then purge from the filesystem what's left. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions """ logger.debug("Executing the 'purge' action.") if config.options is None or config.purge is None: raise ValueError("Purge configuration is not properly filled in.") if config.purge.purgeDirs is not None: for purgeDir in config.purge.purgeDirs: purgeList = PurgeItemList() purgeList.addDirContents(purgeDir.absolutePath) # add everything within directory purgeList.removeYoungFiles(purgeDir.retainDays) # remove young files *from the list* so they won't be purged purgeList.purgeItems() # remove remaining items from the filesystem logger.info("Executed the 'purge' action successfully.") ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/rebuild.py0000644000000000000000000001431114567004737017632 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements the standard 'rebuild' action. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements the standard 'rebuild' action. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import datetime import logging import os import sys from CedarBackup3.actions.constants import DIR_TIME_FORMAT, STAGE_INDICATOR from CedarBackup3.actions.store import consistencyCheck, writeImage, writeStoreIndicator from CedarBackup3.actions.util import checkMediaState from CedarBackup3.util import deriveDayOfWeek, pathJoin ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.actions.rebuild") ######################################################################## # Public functions ######################################################################## ############################ # executeRebuild() function ############################ # pylint: disable=W0613 def executeRebuild(configPath, options, config): """ Executes the rebuild backup action. This function exists mainly to recreate a disc that has been "trashed" due to media or hardware problems. Note that the "stage complete" indicator isn't checked for this action. Note that the rebuild action and the store action are very similar. The main difference is that while store only stores a single day's staging directory, the rebuild action operates on multiple staging directories. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If there are problems reading or writing files """ logger.debug("Executing the 'rebuild' action.") if sys.platform == "darwin": logger.warning("Warning: the rebuild action is not fully supported on Mac OS X.") logger.warning("See the Cedar Backup software manual for further information.") if config.options is None or config.store is None: raise ValueError("Rebuild configuration is not properly filled in.") if config.store.checkMedia: checkMediaState(config.store) # raises exception if media is not initialized stagingDirs = _findRebuildDirs(config) writeImage(config, True, stagingDirs) if config.store.checkData: if sys.platform == "darwin": logger.warning("Warning: consistency check cannot be run successfully on Mac OS X.") logger.warning("See the Cedar Backup software manual for further information.") else: logger.debug("Running consistency check of media.") consistencyCheck(config, stagingDirs) writeStoreIndicator(config, stagingDirs) logger.info("Executed the 'rebuild' action successfully.") ######################################################################## # Private utility functions ######################################################################## ############################## # _findRebuildDirs() function ############################## def _findRebuildDirs(config): """ Finds the set of directories to be included in a disc rebuild. A the rebuild action is supposed to recreate the "last week's" disc. This won't always be possible if some of the staging directories are missing. However, the general procedure is to look back into the past no further than the previous "starting day of week", and then work forward from there trying to find all of the staging directories between then and now that still exist and have a stage indicator. Args: config: Config object Returns: Correct staging dir, as a dict mapping directory to date suffix Raises: IOError: If we do not find at least one staging directory """ stagingDirs = {} start = deriveDayOfWeek(config.options.startingDay) today = datetime.date.today() if today.weekday() >= start: days = today.weekday() - start + 1 else: days = 7 - (start - today.weekday()) + 1 for i in range(0, days): currentDay = today - datetime.timedelta(days=i) dateSuffix = currentDay.strftime(DIR_TIME_FORMAT) stageDir = pathJoin(config.store.sourceDir, dateSuffix) indicator = pathJoin(stageDir, STAGE_INDICATOR) if os.path.isdir(stageDir) and os.path.exists(indicator): logger.info("Rebuild process will include stage directory [%s]", stageDir) stagingDirs[stageDir] = dateSuffix if len(stagingDirs) == 0: raise IOError("Unable to find any staging directories for rebuild process.") return stagingDirs ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/stage.py0000644000000000000000000003112114567004737017305 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements the standard 'stage' action. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements the standard 'stage' action. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import time from CedarBackup3.actions.constants import DIR_TIME_FORMAT, STAGE_INDICATOR from CedarBackup3.actions.util import writeIndicatorFile from CedarBackup3.peer import LocalPeer, RemotePeer from CedarBackup3.util import changeOwnership, getUidGid, isRunningAsRoot, isStartOfWeek, pathJoin ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.actions.stage") ######################################################################## # Public functions ######################################################################## ########################## # executeStage() function ########################## # pylint: disable=W0613 # noinspection PyTypeChecker def executeStage(configPath, options, config): """ Executes the stage backup action. *Note:* The daily directory is derived once and then we stick with it, just in case a backup happens to span midnite. *Note:* As portions of the stage action is complete, we will write various indicator files so that it's obvious what actions have been completed. Each peer gets a stage indicator in its collect directory, and then the master gets a stage indicator in its daily staging directory. The store process uses the master's stage indicator to decide whether a directory is ready to be stored. Currently, nothing uses the indicator at each peer, and it exists for reference only. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If there are problems reading or writing files """ logger.debug("Executing the 'stage' action.") if config.options is None or config.stage is None: raise ValueError("Stage configuration is not properly filled in.") dailyDir = _getDailyDir(config) localPeers = _getLocalPeers(config) remotePeers = _getRemotePeers(config) allPeers = localPeers + remotePeers stagingDirs = _createStagingDirs(config, dailyDir, allPeers) for peer in allPeers: logger.info("Staging peer [%s].", peer.name) ignoreFailures = _getIgnoreFailuresFlag(options, config, peer) if not peer.checkCollectIndicator(): if not ignoreFailures: logger.error("Peer [%s] was not ready to be staged.", peer.name) else: logger.info("Peer [%s] was not ready to be staged.", peer.name) continue logger.debug("Found collect indicator.") targetDir = stagingDirs[peer.name] if isRunningAsRoot(): # Since we're running as root, we can change ownership ownership = getUidGid(config.options.backupUser, config.options.backupGroup) logger.debug("Using target dir [%s], ownership [%d:%d].", targetDir, ownership[0], ownership[1]) else: # Non-root cannot change ownership, so don't set it ownership = None logger.debug("Using target dir [%s], ownership [None].", targetDir) try: count = peer.stagePeer(targetDir=targetDir, ownership=ownership) # note: utilize effective user's default umask logger.info("Staged %d files for peer [%s].", count, peer.name) peer.writeStageIndicator() except (ValueError, IOError, OSError) as e: logger.error("Error staging [%s]: %s", peer.name, e) writeIndicatorFile(dailyDir, STAGE_INDICATOR, config.options.backupUser, config.options.backupGroup) logger.info("Executed the 'stage' action successfully.") ######################################################################## # Private utility functions ######################################################################## ################################ # _createStagingDirs() function ################################ def _createStagingDirs(config, dailyDir, peers): """ Creates staging directories as required. The main staging directory is the passed in daily directory, something like ``staging/2002/05/23``. Then, individual peers get their own directories, i.e. ``staging/2002/05/23/host``. Args: config: Config object dailyDir: Daily staging directory peers: List of all configured peers Returns: Dictionary mapping peer name to staging directory """ mapping = {} if os.path.isdir(dailyDir): logger.warning("Staging directory [%s] already existed.", dailyDir) else: try: logger.debug("Creating staging directory [%s].", dailyDir) os.makedirs(dailyDir) for path in [dailyDir, pathJoin(dailyDir, ".."), pathJoin(dailyDir, "..", "..")]: changeOwnership(path, config.options.backupUser, config.options.backupGroup) except Exception as e: raise Exception("Unable to create staging directory: %s" % e) for peer in peers: peerDir = pathJoin(dailyDir, peer.name) mapping[peer.name] = peerDir if os.path.isdir(peerDir): logger.warning("Peer staging directory [%s] already existed.", peerDir) else: try: logger.debug("Creating peer staging directory [%s].", peerDir) os.makedirs(peerDir) changeOwnership(peerDir, config.options.backupUser, config.options.backupGroup) except Exception as e: raise Exception("Unable to create staging directory: %s" % e) return mapping ######################################################################## # Private attribute "getter" functions ######################################################################## #################################### # _getIgnoreFailuresFlag() function #################################### def _getIgnoreFailuresFlag(options, config, peer): """ Gets the ignore failures flag based on options, configuration, and peer. Args: options: Options object config: Configuration object peer: Peer to check Returns: Whether to ignore stage failures for this peer """ logger.debug("Ignore failure mode for this peer: %s", peer.ignoreFailureMode) if peer.ignoreFailureMode is None or peer.ignoreFailureMode == "none": return False elif peer.ignoreFailureMode == "all": return True else: if options.full or isStartOfWeek(config.options.startingDay): return peer.ignoreFailureMode == "weekly" else: return peer.ignoreFailureMode == "daily" ########################## # _getDailyDir() function ########################## def _getDailyDir(config): """ Gets the daily staging directory. This is just a directory in the form ``staging/YYYY/MM/DD``, i.e. ``staging/2000/10/07``, except it will be an absolute path based on ``config.stage.targetDir``. Args: config: Config object Returns: Path of daily staging directory """ dailyDir = pathJoin(config.stage.targetDir, time.strftime(DIR_TIME_FORMAT)) logger.debug("Daily staging directory is [%s].", dailyDir) return dailyDir ############################ # _getLocalPeers() function ############################ def _getLocalPeers(config): """ Return a list of :any:`LocalPeer` objects based on configuration. Args: config: Config object Returns: List of :any:`LocalPeer` objects """ localPeers = [] configPeers = None if config.stage.hasPeers(): logger.debug("Using list of local peers from stage configuration.") configPeers = config.stage.localPeers elif config.peers is not None and config.peers.hasPeers(): logger.debug("Using list of local peers from peers configuration.") configPeers = config.peers.localPeers if configPeers is not None: for peer in configPeers: localPeer = LocalPeer(peer.name, peer.collectDir, peer.ignoreFailureMode) localPeers.append(localPeer) logger.debug("Found local peer: [%s]", localPeer.name) return localPeers ############################# # _getRemotePeers() function ############################# def _getRemotePeers(config): """ Return a list of :any:`RemotePeer` objects based on configuration. Args: config: Config object Returns: List of :any:`RemotePeer` objects """ remotePeers = [] configPeers = None if config.stage.hasPeers(): logger.debug("Using list of remote peers from stage configuration.") configPeers = config.stage.remotePeers elif config.peers is not None and config.peers.hasPeers(): logger.debug("Using list of remote peers from peers configuration.") configPeers = config.peers.remotePeers if configPeers is not None: for peer in configPeers: remoteUser = _getRemoteUser(config, peer) localUser = _getLocalUser(config) rcpCommand = _getRcpCommand(config, peer) remotePeer = RemotePeer( peer.name, peer.collectDir, config.options.workingDir, remoteUser, rcpCommand, localUser, ignoreFailureMode=peer.ignoreFailureMode, ) remotePeers.append(remotePeer) logger.debug("Found remote peer: [%s]", remotePeer.name) return remotePeers ############################ # _getRemoteUser() function ############################ def _getRemoteUser(config, remotePeer): """ Gets the remote user associated with a remote peer. Use peer's if possible, otherwise take from options section. Args: config: Config object remotePeer: Configuration-style remote peer object Returns: Name of remote user associated with remote peer """ if remotePeer.remoteUser is None: return config.options.backupUser return remotePeer.remoteUser ########################### # _getLocalUser() function ########################### def _getLocalUser(config): """ Gets the remote user associated with a remote peer. Args: config: Config object Returns: Name of local user that should be used """ if not isRunningAsRoot(): return None return config.options.backupUser ############################ # _getRcpCommand() function ############################ def _getRcpCommand(config, remotePeer): """ Gets the RCP command associated with a remote peer. Use peer's if possible, otherwise take from options section. Args: config: Config object remotePeer: Configuration-style remote peer object Returns: RCP command associated with remote peer """ if remotePeer.rcpCommand is None: return config.options.rcpCommand return remotePeer.rcpCommand ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/store.py0000644000000000000000000004262714567004737017353 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements the standard 'store' action. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements the standard 'store' action. :author: Kenneth J. Pronovici :author: Dmitry Rutsky """ ######################################################################## # Imported modules ######################################################################## import datetime import logging import os import sys import tempfile from CedarBackup3.actions.constants import DIR_TIME_FORMAT, STAGE_INDICATOR, STORE_INDICATOR from CedarBackup3.actions.util import buildMediaLabel, checkMediaState, createWriter, writeIndicatorFile from CedarBackup3.filesystem import compareContents from CedarBackup3.util import displayBytes, isStartOfWeek, mount, pathJoin, unmount ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.actions.store") ######################################################################## # Public functions ######################################################################## ########################## # executeStore() function ########################## # pylint: disable=W0613 def executeStore(configPath, options, config): """ Executes the store backup action. *Note:* The rebuild action and the store action are very similar. The main difference is that while store only stores a single day's staging directory, the rebuild action operates on multiple staging directories. *Note:* When the store action is complete, we will write a store indicator to the daily staging directory we used, so it's obvious that the store action has completed. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If there are problems reading or writing files """ logger.debug("Executing the 'store' action.") if sys.platform == "darwin": logger.warning("Warning: the store action is not fully supported on Mac OS X.") logger.warning("See the Cedar Backup software manual for further information.") if config.options is None or config.store is None: raise ValueError("Store configuration is not properly filled in.") if config.store.checkMedia: checkMediaState(config.store) # raises exception if media is not initialized rebuildMedia = options.full logger.debug("Rebuild media flag [%s]", rebuildMedia) todayIsStart = isStartOfWeek(config.options.startingDay) stagingDirs = _findCorrectDailyDir(options, config) writeImageBlankSafe(config, rebuildMedia, todayIsStart, config.store.blankBehavior, stagingDirs) if config.store.checkData: if sys.platform == "darwin": logger.warning("Warning: consistency check cannot be run successfully on Mac OS X.") logger.warning("See the Cedar Backup software manual for further information.") else: logger.debug("Running consistency check of media.") consistencyCheck(config, stagingDirs) writeStoreIndicator(config, stagingDirs) logger.info("Executed the 'store' action successfully.") ######################## # writeImage() function ######################## def writeImage(config, newDisc, stagingDirs): """ Builds and writes an ISO image containing the indicated stage directories. The generated image will contain each of the staging directories listed in ``stagingDirs``. The directories will be placed into the image at the root by date, so staging directory ``/opt/stage/2005/02/10`` will be placed into the disc at ``/2005/02/10``. *Note:* This function is implemented in terms of :any:`writeImageBlankSafe`. The ``newDisc`` flag is passed in for both ``rebuildMedia`` and ``todayIsStart``. Args: config: Config object newDisc: Indicates whether the disc should be re-initialized stagingDirs: Dictionary mapping directory path to date suffix Raises: ValueError: Under many generic error conditions IOError: If there is a problem writing the image to disc """ writeImageBlankSafe(config, newDisc, newDisc, None, stagingDirs) ################################# # writeImageBlankSafe() function ################################# def writeImageBlankSafe(config, rebuildMedia, todayIsStart, blankBehavior, stagingDirs): """ Builds and writes an ISO image containing the indicated stage directories. The generated image will contain each of the staging directories listed in ``stagingDirs``. The directories will be placed into the image at the root by date, so staging directory ``/opt/stage/2005/02/10`` will be placed into the disc at ``/2005/02/10``. The media will always be written with a media label specific to Cedar Backup. This function is similar to :any:`writeImage`, but tries to implement a smarter blanking strategy. First, the media is always blanked if the ``rebuildMedia`` flag is true. Then, if ``rebuildMedia`` is false, blanking behavior and ``todayIsStart`` come into effect:: If no blanking behavior is specified, and it is the start of the week, the disc will be blanked If blanking behavior is specified, and either the blank mode is "daily" or the blank mode is "weekly" and it is the start of the week, then the disc will be blanked if it looks like the weekly backup will not fit onto the media. Otherwise, the disc will not be blanked How do we decide whether the weekly backup will fit onto the media? That is what the blanking factor is used for. The following formula is used:: will backup fit? = (bytes available / (1 + bytes required) <= blankFactor The blanking factor will vary from setup to setup, and will probably require some experimentation to get it right. Args: config: Config object rebuildMedia: Indicates whether media should be rebuilt todayIsStart: Indicates whether today is the starting day of the week blankBehavior: Blank behavior from configuration, or ``None`` to use default behavior stagingDirs: Dictionary mapping directory path to date suffix Raises: ValueError: Under many generic error conditions IOError: If there is a problem writing the image to disc """ mediaLabel = buildMediaLabel() writer = createWriter(config) writer.initializeImage(True, config.options.workingDir, mediaLabel) # default value for newDisc for stageDir in list(stagingDirs.keys()): logger.debug("Adding stage directory [%s].", stageDir) dateSuffix = stagingDirs[stageDir] writer.addImageEntry(stageDir, dateSuffix) newDisc = _getNewDisc(writer, rebuildMedia, todayIsStart, blankBehavior) writer.setImageNewDisc(newDisc) writer.writeImage() def _getNewDisc(writer, rebuildMedia, todayIsStart, blankBehavior): """ Gets a value for the newDisc flag based on blanking factor rules. The blanking factor rules are described above by :any:`writeImageBlankSafe`. Args: writer: Previously configured image writer containing image entries rebuildMedia: Indicates whether media should be rebuilt todayIsStart: Indicates whether today is the starting day of the week blankBehavior: Blank behavior from configuration, or ``None`` to use default behavior Returns: newDisc flag to be set on writer """ newDisc = False if rebuildMedia: newDisc = True logger.debug("Setting new disc flag based on rebuildMedia flag.") else: if blankBehavior is None: logger.debug("Default media blanking behavior is in effect.") if todayIsStart: newDisc = True logger.debug("Setting new disc flag based on todayIsStart.") else: # note: validation says we can assume that behavior is fully filled in if it exists at all logger.debug("Optimized media blanking behavior is in effect based on configuration.") if blankBehavior.blankMode == "daily" or (blankBehavior.blankMode == "weekly" and todayIsStart): logger.debug("New disc flag will be set based on blank factor calculation.") blankFactor = float(blankBehavior.blankFactor) logger.debug("Configured blanking factor: %.2f", blankFactor) available = writer.retrieveCapacity().bytesAvailable logger.debug("Bytes available: %s", displayBytes(available)) required = writer.getEstimatedImageSize() logger.debug("Bytes required: %s", displayBytes(required)) ratio = available / (1.0 + required) logger.debug("Calculated ratio: %.2f", ratio) newDisc = ratio <= blankFactor logger.debug("%.2f <= %.2f ? %s", ratio, blankFactor, newDisc) else: logger.debug("No blank factor calculation is required based on configuration.") logger.debug("New disc flag [%s].", newDisc) return newDisc ################################# # writeStoreIndicator() function ################################# def writeStoreIndicator(config, stagingDirs): """ Writes a store indicator file into staging directories. The store indicator is written into each of the staging directories when either a store or rebuild action has written the staging directory to disc. Args: config: Config object stagingDirs: Dictionary mapping directory path to date suffix """ for stagingDir in list(stagingDirs.keys()): writeIndicatorFile(stagingDir, STORE_INDICATOR, config.options.backupUser, config.options.backupGroup) ############################## # consistencyCheck() function ############################## def consistencyCheck(config, stagingDirs): """ Runs a consistency check against media in the backup device. It seems that sometimes, it's possible to create a corrupted multisession disc (i.e. one that cannot be read) although no errors were encountered while writing the disc. This consistency check makes sure that the data read from disc matches the data that was used to create the disc. The function mounts the device at a temporary mount point in the working directory, and then compares the indicated staging directories in the staging directory and on the media. The comparison is done via functionality in ``filesystem.py``. If no exceptions are thrown, there were no problems with the consistency check. A positive confirmation of "no problems" is also written to the log with ``info`` priority. @warning: The implementation of this function is very UNIX-specific. Args: config: Config object stagingDirs: Dictionary mapping directory path to date suffix Raises: ValueError: If the two directories are not equivalent IOError: If there is a problem working with the media """ logger.debug("Running consistency check.") mountPoint = tempfile.mkdtemp(dir=config.options.workingDir) try: mount(config.store.devicePath, mountPoint, "iso9660") for stagingDir in list(stagingDirs.keys()): discDir = pathJoin(mountPoint, stagingDirs[stagingDir]) logger.debug("Checking [%s] vs. [%s].", stagingDir, discDir) compareContents(stagingDir, discDir, verbose=True) logger.info("Consistency check completed for [%s]. No problems found.", stagingDir) finally: unmount(mountPoint, True, 5, 1) # try 5 times, and remove mount point when done ######################################################################## # Private utility functions ######################################################################## ######################### # _findCorrectDailyDir() ######################### def _findCorrectDailyDir(options, config): """ Finds the correct daily staging directory to be written to disk. In Cedar Backup v1.0, we assumed that the correct staging directory matched the current date. However, that has problems. In particular, it breaks down if collect is on one side of midnite and stage is on the other, or if certain processes span midnite. For v2.0, I'm trying to be smarter. I'll first check the current day. If that directory is found, it's good enough. If it's not found, I'll look for a valid directory from the day before or day after I{which has not yet been staged, according to the stage indicator file}. The first one I find, I'll use. If I use a directory other than for the current day *and* ``config.store.warnMidnite`` is set, a warning will be put in the log. There is one exception to this rule. If the ``options.full`` flag is set, then the special "span midnite" logic will be disabled and any existing store indicator will be ignored. I did this because I think that most users who run ``cback3 --full store`` twice in a row expect the command to generate two identical discs. With the other rule in place, running that command twice in a row could result in an error ("no unstored directory exists") or could even cause a completely unexpected directory to be written to disc (if some previous day's contents had not yet been written). *Note:* This code is probably longer and more verbose than it needs to be, but at least it's straightforward. Args: options: Options object config: Config object Returns: Correct staging dir, as a dict mapping directory to date suffix Raises: IOError: If the staging directory cannot be found """ oneDay = datetime.timedelta(days=1) today = datetime.date.today() yesterday = today - oneDay tomorrow = today + oneDay todayDate = today.strftime(DIR_TIME_FORMAT) yesterdayDate = yesterday.strftime(DIR_TIME_FORMAT) tomorrowDate = tomorrow.strftime(DIR_TIME_FORMAT) todayPath = pathJoin(config.stage.targetDir, todayDate) yesterdayPath = pathJoin(config.stage.targetDir, yesterdayDate) tomorrowPath = pathJoin(config.stage.targetDir, tomorrowDate) todayStageInd = pathJoin(todayPath, STAGE_INDICATOR) yesterdayStageInd = pathJoin(yesterdayPath, STAGE_INDICATOR) tomorrowStageInd = pathJoin(tomorrowPath, STAGE_INDICATOR) todayStoreInd = pathJoin(todayPath, STORE_INDICATOR) yesterdayStoreInd = pathJoin(yesterdayPath, STORE_INDICATOR) tomorrowStoreInd = pathJoin(tomorrowPath, STORE_INDICATOR) if options.full: if os.path.isdir(todayPath) and os.path.exists(todayStageInd): logger.info("Store process will use current day's stage directory [%s]", todayPath) return {todayPath: todayDate} raise IOError("Unable to find staging directory to store (only tried today due to full option).") else: if os.path.isdir(todayPath) and os.path.exists(todayStageInd) and not os.path.exists(todayStoreInd): logger.info("Store process will use current day's stage directory [%s]", todayPath) return {todayPath: todayDate} elif os.path.isdir(yesterdayPath) and os.path.exists(yesterdayStageInd) and not os.path.exists(yesterdayStoreInd): logger.info("Store process will use previous day's stage directory [%s]", yesterdayPath) if config.store.warnMidnite: logger.warning("Warning: store process crossed midnite boundary to find data.") return {yesterdayPath: yesterdayDate} elif os.path.isdir(tomorrowPath) and os.path.exists(tomorrowStageInd) and not os.path.exists(tomorrowStoreInd): logger.info("Store process will use next day's stage directory [%s]", tomorrowPath) if config.store.warnMidnite: logger.warning("Warning: store process crossed midnite boundary to find data.") return {tomorrowPath: tomorrowDate} raise IOError("Unable to find unused staging directory to store (tried today, yesterday, tomorrow).") ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/util.py0000644000000000000000000003226614567004737017172 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements action-related utilities # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements action-related utilities :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import tempfile import time from CedarBackup3.actions.constants import INDICATOR_PATTERN from CedarBackup3.config import DEFAULT_DEVICE_TYPE, DEFAULT_MEDIA_TYPE, REWRITABLE_MEDIA_TYPES from CedarBackup3.filesystem import FilesystemList from CedarBackup3.util import changeOwnership, deviceMounted, pathJoin from CedarBackup3.writers.cdwriter import MEDIA_CDR_74, MEDIA_CDR_80, MEDIA_CDRW_74, MEDIA_CDRW_80, CdWriter from CedarBackup3.writers.dvdwriter import MEDIA_DVDPLUSR, MEDIA_DVDPLUSRW, DvdWriter from CedarBackup3.writers.util import readMediaLabel ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.actions.util") MEDIA_LABEL_PREFIX = "CEDAR BACKUP" ######################################################################## # Public utility functions ######################################################################## ########################### # findDailyDirs() function ########################### def findDailyDirs(stagingDir, indicatorFile): """ Returns a list of all daily staging directories that do not contain the indicated indicator file. Returns: List of absolute paths to daily staging directories """ results = FilesystemList() yearDirs = FilesystemList() yearDirs.excludeFiles = True yearDirs.excludeLinks = True yearDirs.addDirContents(path=stagingDir, recursive=False, addSelf=False) for yearDir in yearDirs: monthDirs = FilesystemList() monthDirs.excludeFiles = True monthDirs.excludeLinks = True monthDirs.addDirContents(path=yearDir, recursive=False, addSelf=False) for monthDir in monthDirs: dailyDirs = FilesystemList() dailyDirs.excludeFiles = True dailyDirs.excludeLinks = True dailyDirs.addDirContents(path=monthDir, recursive=False, addSelf=False) for dailyDir in dailyDirs: if os.path.exists(pathJoin(dailyDir, indicatorFile)): logger.debug("Skipping directory [%s]; contains %s.", dailyDir, indicatorFile) else: logger.debug("Adding [%s] to list of daily directories.", dailyDir) results.append(dailyDir) # just put it in the list, no fancy operations return results ########################### # createWriter() function ########################### def createWriter(config): """ Creates a writer object based on current configuration. This function creates and returns a writer based on configuration. This is done to abstract action functionality from knowing what kind of writer is in use. Since all writers implement the same interface, there's no need for actions to care which one they're working with. Currently, the ``cdwriter`` and ``dvdwriter`` device types are allowed. An exception will be raised if any other device type is used. This function also checks to make sure that the device isn't mounted before creating a writer object for it. Experience shows that sometimes if the device is mounted, we have problems with the backup. We may as well do the check here first, before instantiating the writer. Args: config: Config object Returns: Writer that can be used to write a directory to some media Raises: ValueError: If there is a problem getting the writer IOError: If there is a problem creating the writer object """ devicePath = config.store.devicePath deviceScsiId = config.store.deviceScsiId driveSpeed = config.store.driveSpeed noEject = config.store.noEject refreshMediaDelay = config.store.refreshMediaDelay ejectDelay = config.store.ejectDelay deviceType = _getDeviceType(config) mediaType = _getMediaType(config) if deviceMounted(devicePath): raise IOError("Device [%s] is currently mounted." % (devicePath)) if deviceType == "cdwriter": return CdWriter(devicePath, deviceScsiId, driveSpeed, mediaType, noEject, refreshMediaDelay, ejectDelay) elif deviceType == "dvdwriter": return DvdWriter(devicePath, deviceScsiId, driveSpeed, mediaType, noEject, refreshMediaDelay, ejectDelay) else: raise ValueError("Device type [%s] is invalid." % deviceType) ################################ # writeIndicatorFile() function ################################ def writeIndicatorFile(targetDir, indicatorFile, backupUser, backupGroup): """ Writes an indicator file into a target directory. Args: targetDir: Target directory in which to write indicator indicatorFile: Name of the indicator file backupUser: User that indicator file should be owned by backupGroup: Group that indicator file should be owned by Raises: IOException: If there is a problem writing the indicator file """ filename = pathJoin(targetDir, indicatorFile) logger.debug("Writing indicator file [%s].", filename) try: with open(filename, "w") as f: # pylint: disable=unspecified-encoding f.write("") changeOwnership(filename, backupUser, backupGroup) except Exception as e: logger.error("Error writing [%s]: %s", filename, e) raise e ############################ # getBackupFiles() function ############################ def getBackupFiles(targetDir): """ Gets a list of backup files in a target directory. Files that match INDICATOR_PATTERN (i.e. ``"cback.store"``, ``"cback.stage"``, etc.) are assumed to be indicator files and are ignored. Args: targetDir: Directory to look in Returns: List of backup files in the directory Raises: ValueError: If the target directory does not exist """ if not os.path.isdir(targetDir): raise ValueError("Target directory [%s] is not a directory or does not exist." % targetDir) fileList = FilesystemList() fileList.excludeDirs = True fileList.excludeLinks = True fileList.excludeBasenamePatterns = INDICATOR_PATTERN fileList.addDirContents(targetDir) return fileList #################### # checkMediaState() #################### def checkMediaState(storeConfig): """ Checks state of the media in the backup device to confirm whether it has been initialized for use with Cedar Backup. We can tell whether the media has been initialized by looking at its media label. If the media label starts with MEDIA_LABEL_PREFIX, then it has been initialized. The check varies depending on whether the media is rewritable or not. For non-rewritable media, we also accept a ``None`` media label, since this kind of media cannot safely be initialized. Args: storeConfig: Store configuration Raises: ValueError: If media is not initialized """ mediaLabel = readMediaLabel(storeConfig.devicePath) if storeConfig.mediaType in REWRITABLE_MEDIA_TYPES: if mediaLabel is None: raise ValueError("Media has not been initialized: no media label available") elif not mediaLabel.startswith(MEDIA_LABEL_PREFIX): raise ValueError("Media has not been initialized: unrecognized media label [%s]" % mediaLabel) else: if mediaLabel is None: logger.info("Media has no media label; assuming OK since media is not rewritable.") elif not mediaLabel.startswith(MEDIA_LABEL_PREFIX): raise ValueError("Media has not been initialized: unrecognized media label [%s]" % mediaLabel) ######################### # initializeMediaState() ######################### def initializeMediaState(config): """ Initializes state of the media in the backup device so Cedar Backup can recognize it. This is done by writing an mostly-empty image (it contains a "Cedar Backup" directory) to the media with a known media label. *Note:* Only rewritable media (CD-RW, DVD+RW) can be initialized. It doesn't make any sense to initialize media that cannot be rewritten (CD-R, DVD+R), since Cedar Backup would then not be able to use that media for a backup. Args: config: Cedar Backup configuration Raises: ValueError: If media could not be initialized ValueError: If the configured media type is not rewritable """ if config.store.mediaType not in REWRITABLE_MEDIA_TYPES: raise ValueError("Only rewritable media types can be initialized.") mediaLabel = buildMediaLabel() writer = createWriter(config) writer.refreshMedia() writer.initializeImage(True, config.options.workingDir, mediaLabel) # always create a new disc tempdir = tempfile.mkdtemp(dir=config.options.workingDir) try: writer.addImageEntry(tempdir, "CedarBackup") writer.writeImage() finally: if os.path.exists(tempdir): try: os.rmdir(tempdir) except: pass #################### # buildMediaLabel() #################### def buildMediaLabel(): """ Builds a media label to be used on Cedar Backup media. Returns: Media label as a string """ currentDate = time.strftime("%d-%b-%Y").upper() return "%s %s" % (MEDIA_LABEL_PREFIX, currentDate) ######################################################################## # Private attribute "getter" functions ######################################################################## ############################ # _getDeviceType() function ############################ def _getDeviceType(config): """ Gets the device type that should be used for storing. Use the configured device type if not ``None``, otherwise use :any:`config.DEFAULT_DEVICE_TYPE`. Args: config: Config object Returns: Device type to be used """ if config.store.deviceType is None: deviceType = DEFAULT_DEVICE_TYPE else: deviceType = config.store.deviceType logger.debug("Device type is [%s]", deviceType) return deviceType ########################### # _getMediaType() function ########################### def _getMediaType(config): """ Gets the media type that should be used for storing. Use the configured media type if not ``None``, otherwise use ``DEFAULT_MEDIA_TYPE``. Once we figure out what configuration value to use, we return a media type value that is valid in one of the supported writers:: MEDIA_CDR_74 MEDIA_CDRW_74 MEDIA_CDR_80 MEDIA_CDRW_80 MEDIA_DVDPLUSR MEDIA_DVDPLUSRW Args: config: Config object Returns: Media type to be used as a writer media type value Raises: ValueError: If the media type is not valid """ if config.store.mediaType is None: mediaType = DEFAULT_MEDIA_TYPE else: mediaType = config.store.mediaType if mediaType == "cdr-74": logger.debug("Media type is MEDIA_CDR_74.") return MEDIA_CDR_74 elif mediaType == "cdrw-74": logger.debug("Media type is MEDIA_CDRW_74.") return MEDIA_CDRW_74 elif mediaType == "cdr-80": logger.debug("Media type is MEDIA_CDR_80.") return MEDIA_CDR_80 elif mediaType == "cdrw-80": logger.debug("Media type is MEDIA_CDRW_80.") return MEDIA_CDRW_80 elif mediaType == "dvd+r": logger.debug("Media type is MEDIA_DVDPLUSR.") return MEDIA_DVDPLUSR elif mediaType == "dvd+rw": logger.debug("Media type is MEDIA_DVDPLUSRW.") return MEDIA_DVDPLUSRW else: raise ValueError("Media type [%s] is not valid." % mediaType) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/actions/validate.py0000644000000000000000000002750414567004737020005 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements the standard 'validate' action. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements the standard 'validate' action. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os from CedarBackup3.actions.util import createWriter from CedarBackup3.util import getFunctionReference, getUidGid ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.actions.validate") ######################################################################## # Public functions ######################################################################## ############################# # executeValidate() function ############################# # pylint: disable=W0613 def executeValidate(configPath, options, config): """ Executes the validate action. This action validates each of the individual sections in the config file. This is a "runtime" validation. The config file itself is already valid in a structural sense, so what we check here that is that we can actually use the configuration without any problems. There's a separate validation function for each of the configuration sections. Each validation function returns a true/false indication for whether configuration was valid, and then logs any configuration problems it finds. This way, one pass over configuration indicates most or all of the obvious problems, rather than finding just one problem at a time. Any reported problems will be logged at the ERROR level normally, or at the INFO level if the quiet flag is enabled. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: If some configuration value is invalid """ logger.debug("Executing the 'validate' action.") if options.quiet: logfunc = logger.info # info so it goes to the log else: logfunc = logger.error # error so it goes to the screen valid = True valid &= _validateReference(config, logfunc) valid &= _validateOptions(config, logfunc) valid &= _validateCollect(config, logfunc) valid &= _validateStage(config, logfunc) valid &= _validateStore(config, logfunc) valid &= _validatePurge(config, logfunc) valid &= _validateExtensions(config, logfunc) if valid: logfunc("Configuration is valid.") else: logfunc("Configuration is not valid.") ######################################################################## # Private utility functions ######################################################################## ####################### # _checkDir() function ####################### def _checkDir(path, writable, logfunc, prefix): """ Checks that the indicated directory is OK. The path must exist, must be a directory, must be readable and executable, and must optionally be writable. Args: path: Path to check writable: Check that path is writable logfunc: Function to use for logging errors prefix: Prefix to use on logged errors Returns: True if the directory is OK, False otherwise """ if not os.path.exists(path): logfunc("%s [%s] does not exist." % (prefix, path)) return False if not os.path.isdir(path): logfunc("%s [%s] is not a directory." % (prefix, path)) return False if not os.access(path, os.R_OK): logfunc("%s [%s] is not readable." % (prefix, path)) return False if not os.access(path, os.X_OK): logfunc("%s [%s] is not executable." % (prefix, path)) return False if writable and not os.access(path, os.W_OK): logfunc("%s [%s] is not writable." % (prefix, path)) return False return True ################################ # _validateReference() function ################################ def _validateReference(config, logfunc): """ Execute runtime validations on reference configuration. We only validate that reference configuration exists at all. Args: config: Program configuration logfunc: Function to use for logging errors Returns: True if configuration is valid, false otherwise """ valid = True if config.reference is None: logfunc("Required reference configuration does not exist.") valid = False return valid ############################## # _validateOptions() function ############################## def _validateOptions(config, logfunc): """ Execute runtime validations on options configuration. The following validations are enforced: - The options section must exist - The working directory must exist and must be writable - The backup user and backup group must exist Args: config: Program configuration logfunc: Function to use for logging errors Returns: True if configuration is valid, false otherwise """ valid = True if config.options is None: logfunc("Required options configuration does not exist.") valid = False else: valid &= _checkDir(config.options.workingDir, True, logfunc, "Working directory") try: getUidGid(config.options.backupUser, config.options.backupGroup) except ValueError: logfunc("Backup user:group [%s:%s] invalid." % (config.options.backupUser, config.options.backupGroup)) valid = False return valid ############################## # _validateCollect() function ############################## def _validateCollect(config, logfunc): """ Execute runtime validations on collect configuration. The following validations are enforced: - The target directory must exist and must be writable - Each of the individual collect directories must exist and must be readable Args: config: Program configuration logfunc: Function to use for logging errors Returns: True if configuration is valid, false otherwise """ valid = True if config.collect is not None: valid &= _checkDir(config.collect.targetDir, True, logfunc, "Collect target directory") if config.collect.collectDirs is not None: for collectDir in config.collect.collectDirs: valid &= _checkDir(collectDir.absolutePath, False, logfunc, "Collect directory") return valid ############################ # _validateStage() function ############################ def _validateStage(config, logfunc): """ Execute runtime validations on stage configuration. The following validations are enforced: - The target directory must exist and must be writable - Each local peer's collect directory must exist and must be readable *Note:* We currently do not validate anything having to do with remote peers, since we don't have a straightforward way of doing it. It would require adding an rsh command rather than just an rcp command to configuration, and that just doesn't seem worth it right now. Args: config: Program configuration logfunc: Function to use for logging errors Returns: True if configuration is valid, False otherwise """ valid = True if config.stage is not None: valid &= _checkDir(config.stage.targetDir, True, logfunc, "Stage target dir ") if config.stage.localPeers is not None: for peer in config.stage.localPeers: valid &= _checkDir(peer.collectDir, False, logfunc, "Local peer collect dir ") return valid ############################ # _validateStore() function ############################ def _validateStore(config, logfunc): """ Execute runtime validations on store configuration. The following validations are enforced: - The source directory must exist and must be readable - The backup device (path and SCSI device) must be valid Args: config: Program configuration logfunc: Function to use for logging errors Returns: True if configuration is valid, False otherwise """ valid = True if config.store is not None: valid &= _checkDir(config.store.sourceDir, False, logfunc, "Store source directory") try: createWriter(config) except ValueError: logfunc("Backup device [%s] [%s] is not valid." % (config.store.devicePath, config.store.deviceScsiId)) valid = False return valid ############################ # _validatePurge() function ############################ def _validatePurge(config, logfunc): """ Execute runtime validations on purge configuration. The following validations are enforced: - Each purge directory must exist and must be writable Args: config: Program configuration logfunc: Function to use for logging errors Returns: True if configuration is valid, False otherwise """ valid = True if config.purge is not None: if config.purge.purgeDirs is not None: for purgeDir in config.purge.purgeDirs: valid &= _checkDir(purgeDir.absolutePath, True, logfunc, "Purge directory") return valid ################################# # _validateExtensions() function ################################# def _validateExtensions(config, logfunc): """ Execute runtime validations on extensions configuration. The following validations are enforced: - Each indicated extension function must exist. Args: config: Program configuration logfunc: Function to use for logging errors Returns: True if configuration is valid, False otherwise """ valid = True if config.extensions is not None: if config.extensions.actions is not None: for action in config.extensions.actions: try: getFunctionReference(action.module, action.function) except ImportError: logfunc("Unable to find function [%s.%s]." % (action.module, action.function)) valid = False except ValueError: logfunc("Function [%s.%s] is not callable." % (action.module, action.function)) valid = False return valid ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/cli.py0000644000000000000000000024052514567004737015323 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides command-line interface implementation. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides command-line interface implementation for the cback3 script. Summary ======= The functionality in this module encapsulates the command-line interface for the cback3 script. The cback3 script itself is very short, basically just an invokation of one function implemented here. That, in turn, makes it simpler to validate the command line interface (for instance, it's easier to run pychecker against a module, and unit tests are easier, too). The objects and functions implemented in this module are probably not useful to any code external to Cedar Backup. Anyone else implementing their own command-line interface would have to reimplement (or at least enhance) all of this anyway. Backwards Compatibility ======================= The command line interface has changed between Cedar Backup 1.x and Cedar Backup 2.x. Some new switches have been added, and the actions have become simple arguments rather than switches (which is a much more standard command line format). Old 1.x command lines are generally no longer valid. Module Attributes ================= Attributes: DEFAULT_CONFIG: The default configuration file DEFAULT_LOGFILE: The default log file path DEFAULT_OWNERSHIP: Default ownership for the logfile DEFAULT_MODE: Default file permissions mode on the logfile VALID_ACTIONS: List of valid actions COMBINE_ACTIONS: List of actions which can be combined with other actions NONCOMBINE_ACTIONS: List of actions which cannot be combined with other actions :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import getopt import logging import os import sys from functools import total_ordering from CedarBackup3.actions.collect import executeCollect from CedarBackup3.actions.initialize import executeInitialize from CedarBackup3.actions.purge import executePurge from CedarBackup3.actions.rebuild import executeRebuild from CedarBackup3.actions.stage import executeStage from CedarBackup3.actions.store import executeStore from CedarBackup3.actions.validate import executeValidate from CedarBackup3.config import Config from CedarBackup3.customize import customizeOverrides from CedarBackup3.peer import RemotePeer from CedarBackup3.release import AUTHOR, EMAIL, VERSION from CedarBackup3.util import ( Diagnostics, DirectedGraph, PathResolverSingleton, encodePath, executeCommand, getFunctionReference, getUidGid, sortDict, splitCommandLine, ) ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.cli") DISK_LOG_FORMAT = "%(asctime)s --> [%(levelname)-7s] %(message)s" DISK_OUTPUT_FORMAT = "%(message)s" SCREEN_LOG_FORMAT = "%(message)s" SCREEN_LOG_STREAM = sys.stdout DATE_FORMAT = "%Y-%m-%dT%H:%M:%S %Z" DEFAULT_CONFIG = "/etc/cback3.conf" DEFAULT_LOGFILE = "/var/log/cback3.log" DEFAULT_OWNERSHIP = ["root", "adm"] DEFAULT_MODE = 0o640 REBUILD_INDEX = 0 # can't run with anything else, anyway VALIDATE_INDEX = 0 # can't run with anything else, anyway INITIALIZE_INDEX = 0 # can't run with anything else, anyway COLLECT_INDEX = 100 STAGE_INDEX = 200 STORE_INDEX = 300 PURGE_INDEX = 400 VALID_ACTIONS = ["collect", "stage", "store", "purge", "rebuild", "validate", "initialize", "all"] COMBINE_ACTIONS = ["collect", "stage", "store", "purge"] NONCOMBINE_ACTIONS = ["rebuild", "validate", "initialize", "all"] SHORT_SWITCHES = "hVbqc:fMNl:o:m:OdsD" LONG_SWITCHES = [ "help", "version", "verbose", "quiet", "config=", "full", "managed", "managed-only", "logfile=", "owner=", "mode=", "output", "debug", "stack", "diagnostics", ] ####################################################################### # Public functions ####################################################################### ################# # cli() function ################# def cli(): """ Implements the command-line interface for the ``cback3`` script. Essentially, this is the "main routine" for the cback3 script. It does all of the argument processing for the script, and then sets about executing the indicated actions. As a general rule, only the actions indicated on the command line will be executed. We will accept any of the built-in actions and any of the configured extended actions (which makes action list verification a two- step process). The ``'all'`` action has a special meaning: it means that the built-in set of actions (collect, stage, store, purge) will all be executed, in that order. Extended actions will be ignored as part of the ``'all'`` action. Raised exceptions always result in an immediate return. Otherwise, we generally return when all specified actions have been completed. Actions are ignored if the help, version or validate flags are set. A different error code is returned for each type of failure: - ``1``: The Python interpreter version is not supported - ``2``: Error processing command-line arguments - ``3``: Error configuring logging - ``4``: Error parsing indicated configuration file - ``5``: Backup was interrupted with a CTRL-C or similar - ``6``: Error executing specified backup actions *Note:* This function contains a good amount of logging at the INFO level, because this is the right place to document high-level flow of control (i.e. what the command-line options were, what config file was being used, etc.) *Note:* We assume that anything that *must* be seen on the screen is logged at the ERROR level. Errors that occur before logging can be configured are written to ``sys.stderr``. Returns: Error code as described above """ try: if list(map(int, [sys.version_info[0], sys.version_info[1]])) < [3, 8]: sys.stderr.write("Python 3 version 3.8 or greater required.\n") return 1 except: # sys.version_info isn't available before 2.0 sys.stderr.write("Python 3 version 3.8 or greater required.\n") return 1 try: options = Options(argumentList=sys.argv[1:]) logger.info("Specified command-line actions: %s", options.actions) except Exception as e: _usage() sys.stderr.write(" *** Error: %s\n" % e) return 2 if options.help: _usage() return 0 if options.version: _version() return 0 if options.diagnostics: _diagnostics() return 0 if options.stacktrace: logfile = setupLogging(options) else: try: logfile = setupLogging(options) except Exception as e: sys.stderr.write("Error setting up logging: %s\n" % e) return 3 logger.info("Cedar Backup run started.") logger.info("Options were [%s]", options) logger.info("Logfile is [%s]", logfile) Diagnostics().logDiagnostics(method=logger.info) if options.config is None: logger.debug("Using default configuration file.") configPath = DEFAULT_CONFIG else: logger.debug("Using user-supplied configuration file.") configPath = options.config executeLocal = True executeManaged = False if options.managedOnly: executeLocal = False executeManaged = True if options.managed: executeManaged = True logger.debug("Execute local actions: %s", executeLocal) logger.debug("Execute managed actions: %s", executeManaged) try: logger.info("Configuration path is [%s]", configPath) config = Config(xmlPath=configPath) customizeOverrides(config) setupPathResolver(config) actionSet = _ActionSet(options.actions, config.extensions, config.options, config.peers, executeManaged, executeLocal) except Exception as e: logger.error("Error reading or handling configuration: %s", e) logger.info("Cedar Backup run completed with status 4.") return 4 if options.stacktrace: actionSet.executeActions(configPath, options, config) else: try: actionSet.executeActions(configPath, options, config) except KeyboardInterrupt: logger.error("Backup interrupted.") logger.info("Cedar Backup run completed with status 5.") return 5 except Exception as e: logger.error("Error executing backup: %s", e) logger.info("Cedar Backup run completed with status 6.") return 6 logger.info("Cedar Backup run completed with status 0.") return 0 ######################################################################## # Action-related class definition ######################################################################## #################### # _ActionItem class #################### @total_ordering class _ActionItem(object): """ Class representing a single action to be executed. This class represents a single named action to be executed, and understands how to execute that action. The built-in actions will use only the options and config values. We also pass in the config path so that extension modules can re-parse configuration if they want to, to add in extra information. This class is also where pre-action and post-action hooks are executed. An action item is instantiated in terms of optional pre- and post-action hook objects (config.ActionHook), which are then executed at the appropriate time (if set). *Note:* The comparison operators for this class have been implemented to only compare based on the index and SORT_ORDER value, and ignore all other values. This is so that the action set list can be easily sorted first by type (_ActionItem before _ManagedActionItem) and then by index within type. Attributes: SORT_ORDER: Defines a sort order to order properly between types """ SORT_ORDER = 0 def __init__(self, index, name, preHooks, postHooks, function): """ Default constructor. It's OK to pass ``None`` for ``index``, ``preHooks`` or ``postHooks``, but not for ``name``. Args: index: Index of the item (or ``None``) name: Name of the action that is being executed preHooks: List of pre-action hooks in terms of an ``ActionHook`` object, or ``None`` postHooks: List of post-action hooks in terms of an ``ActionHook`` object, or ``None`` function: Reference to function associated with item """ self.index = index self.name = name self.preHooks = preHooks self.postHooks = postHooks self.function = function def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. The only thing we compare is the item's index. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.index != other.index: if int(self.index or 0) < int(other.index or 0): return -1 else: return 1 else: if self.SORT_ORDER != other.SORT_ORDER: if int(self.SORT_ORDER or 0) < int(other.SORT_ORDER or 0): return -1 else: return 1 return 0 def executeAction(self, configPath, options, config): """ Executes the action associated with an item, including hooks. See class notes for more details on how the action is executed. Args: configPath: Path to configuration file on disk options: Command-line options to be passed to action config: Parsed configuration to be passed to action Raises: Exception: If there is a problem executing the action """ logger.debug("Executing [%s] action.", self.name) if self.preHooks is not None: for hook in self.preHooks: self._executeHook("pre-action", hook) self._executeAction(configPath, options, config) if self.postHooks is not None: for hook in self.postHooks: self._executeHook("post-action", hook) def _executeAction(self, configPath, options, config): """ Executes the action, specifically the function associated with the action. Args: configPath: Path to configuration file on disk options: Command-line options to be passed to action config: Parsed configuration to be passed to action """ name = "%s.%s" % (self.function.__module__, self.function.__name__) logger.debug("Calling action function [%s], execution index [%d]", name, self.index) self.function(configPath, options, config) def _executeHook(self, type, hook): # pylint: disable=W0622 """ Executes a hook command via :any:`util.executeCommand`. Args: type: String describing the type of hook, for logging hook: Hook, in terms of a ``ActionHook`` object """ fields = splitCommandLine(hook.command) logger.debug("Executing %s hook for action %s: %s", type, hook.action, fields) result, output = executeCommand(command=fields[0:1], args=fields[1:], returnOutput=True) if result != 0: logger.error("Hook failed, tail is: %s", "\n %s" % " ".join(output[-10:]) if output else "") raise IOError("Error (%d) executing %s hook for action %s: %s" % (result, type, hook.action, fields)) ########################### # _ManagedActionItem class ########################### @total_ordering class _ManagedActionItem(object): """ Class representing a single action to be executed on a managed peer. This class represents a single named action to be executed, and understands how to execute that action. Actions to be executed on a managed peer rely on peer configuration and on the full-backup flag. All other configuration takes place on the remote peer itself. *Note:* The comparison operators for this class have been implemented to only compare based on the index and SORT_ORDER value, and ignore all other values. This is so that the action set list can be easily sorted first by type (_ActionItem before _ManagedActionItem) and then by index within type. Attributes: SORT_ORDER: Defines a sort order to order properly between types """ SORT_ORDER = 1 def __init__(self, index, name, remotePeers): """ Default constructor. Args: index: Index of the item (or ``None``) name: Name of the action that is being executed remotePeers: List of remote peers on which to execute the action """ self.index = index self.name = name self.remotePeers = remotePeers def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. The only thing we compare is the item's index. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.index != other.index: if int(self.index or 0) < int(other.index or 0): return -1 else: return 1 else: if self.SORT_ORDER != other.SORT_ORDER: if int(self.SORT_ORDER or 0) < int(other.SORT_ORDER or 0): return -1 else: return 1 return 0 # pylint: disable=W0613 def executeAction(self, configPath, options, config): """ Executes the managed action associated with an item. *Note:* Only options.full is actually used. The rest of the arguments exist to satisfy the ActionItem iterface. *Note:* Errors here result in a message logged to ERROR, but no thrown exception. The analogy is the stage action where a problem with one host should not kill the entire backup. Since we're logging an error, the administrator will get an email. Args: configPath: Path to configuration file on disk options: Command-line options to be passed to action config: Parsed configuration to be passed to action Raises: Exception: If there is a problem executing the action """ for peer in self.remotePeers: logger.debug("Executing managed action [%s] on peer [%s].", self.name, peer.name) try: peer.executeManagedAction(self.name, options.full) except IOError as e: logger.error(e) # log the message and go on, so we don't kill the backup ################### # _ActionSet class ################### class _ActionSet(object): """ Class representing a set of local actions to be executed. This class does four different things. First, it ensures that the actions specified on the command-line are sensible. The command-line can only list either built-in actions or extended actions specified in configuration. Also, certain actions (in :any:`NONCOMBINE_ACTIONS`) cannot be combined with other actions. Second, the class enforces an execution order on the specified actions. Any time actions are combined on the command line (either built-in actions or extended actions), we must make sure they get executed in a sensible order. Third, the class ensures that any pre-action or post-action hooks are scheduled and executed appropriately. Hooks are configured by building a dictionary mapping between hook action name and command. Pre-action hooks are executed immediately before their associated action, and post-action hooks are executed immediately after their associated action. Finally, the class properly interleaves local and managed actions so that the same action gets executed first locally and then on managed peers. """ def __init__(self, actions, extensions, options, peers, managed, local): """ Constructor for the ``_ActionSet`` class. This is kind of ugly, because the constructor has to set up a lot of data before being able to do anything useful. The following data structures are initialized based on the input: - ``extensionNames``: List of extensions available in configuration - ``preHookMap``: Mapping from action name to list of ``PreActionHook`` - ``postHookMap``: Mapping from action name to list of ``PostActionHook`` - ``functionMap``: Mapping from action name to Python function - ``indexMap``: Mapping from action name to execution index - ``peerMap``: Mapping from action name to set of ``RemotePeer`` - ``actionMap``: Mapping from action name to ``_ActionItem`` Once these data structures are set up, the command line is validated to make sure only valid actions have been requested, and in a sensible combination. Then, all of the data is used to build ``self.actionSet``, the set action items to be executed by ``executeActions()``. This list might contain either ``_ActionItem`` or ``_ManagedActionItem``. Args: actions: Names of actions specified on the command-line extensions: Extended action configuration (i.e. config.extensions) options: Options configuration (i.e. config.options) peers: Peers configuration (i.e. config.peers) managed: Whether to include managed actions in the set local: Whether to include local actions in the set Raises: ValueError: If one of the specified actions is invalid """ extensionNames = _ActionSet._deriveExtensionNames(extensions) (preHookMap, postHookMap) = _ActionSet._buildHookMaps(options.hooks) functionMap = _ActionSet._buildFunctionMap(extensions) indexMap = _ActionSet._buildIndexMap(extensions) peerMap = _ActionSet._buildPeerMap(options, peers) actionMap = _ActionSet._buildActionMap( managed, local, extensionNames, functionMap, indexMap, preHookMap, postHookMap, peerMap ) _ActionSet._validateActions(actions, extensionNames) self.actionSet = _ActionSet._buildActionSet(actions, actionMap) @staticmethod def _deriveExtensionNames(extensions): """ Builds a list of extended actions that are available in configuration. Args: extensions: Extended action configuration (i.e. config.extensions) Returns: List of extended action names """ extensionNames = [] if extensions is not None and extensions.actions is not None: for action in extensions.actions: extensionNames.append(action.name) return extensionNames @staticmethod def _buildHookMaps(hooks): """ Build two mappings from action name to configured ``ActionHook``. Args: hooks: List of pre- and post-action hooks (i.e. config.options.hooks) Returns: Tuple of (pre hook dictionary, post hook dictionary) """ preHookMap = {} postHookMap = {} if hooks is not None: for hook in hooks: if hook.before: if hook.action not in preHookMap: preHookMap[hook.action] = [] preHookMap[hook.action].append(hook) elif hook.after: if hook.action not in postHookMap: postHookMap[hook.action] = [] postHookMap[hook.action].append(hook) return (preHookMap, postHookMap) @staticmethod def _buildFunctionMap(extensions): """ Builds a mapping from named action to action function. Args: extensions: Extended action configuration (i.e. config.extensions) Returns: Dictionary mapping action to function """ functionMap = {} functionMap["rebuild"] = executeRebuild functionMap["validate"] = executeValidate functionMap["initialize"] = executeInitialize functionMap["collect"] = executeCollect functionMap["stage"] = executeStage functionMap["store"] = executeStore functionMap["purge"] = executePurge if extensions is not None and extensions.actions is not None: for action in extensions.actions: functionMap[action.name] = getFunctionReference(action.module, action.function) return functionMap @staticmethod def _buildIndexMap(extensions): """ Builds a mapping from action name to proper execution index. If extensions configuration is ``None``, or there are no configured extended actions, the ordering dictionary will only include the built-in actions and their standard indices. Otherwise, if the extensions order mode is ``None`` or ``"index"``, actions will scheduled by explicit index; and if the extensions order mode is ``"dependency"``, actions will be scheduled using a dependency graph. Args: extensions: Extended action configuration (i.e. config.extensions) Returns: Dictionary mapping action name to integer execution index """ indexMap = {} if extensions is None or extensions.actions is None or extensions.actions == []: logger.info("Action ordering will use 'index' order mode.") indexMap["rebuild"] = REBUILD_INDEX indexMap["validate"] = VALIDATE_INDEX indexMap["initialize"] = INITIALIZE_INDEX indexMap["collect"] = COLLECT_INDEX indexMap["stage"] = STAGE_INDEX indexMap["store"] = STORE_INDEX indexMap["purge"] = PURGE_INDEX logger.debug("Completed filling in action indices for built-in actions.") logger.info("Action order will be: %s", sortDict(indexMap)) else: if extensions.orderMode is None or extensions.orderMode == "index": logger.info("Action ordering will use 'index' order mode.") indexMap["rebuild"] = REBUILD_INDEX indexMap["validate"] = VALIDATE_INDEX indexMap["initialize"] = INITIALIZE_INDEX indexMap["collect"] = COLLECT_INDEX indexMap["stage"] = STAGE_INDEX indexMap["store"] = STORE_INDEX indexMap["purge"] = PURGE_INDEX logger.debug("Completed filling in action indices for built-in actions.") for action in extensions.actions: indexMap[action.name] = action.index logger.debug("Completed filling in action indices for extended actions.") logger.info("Action order will be: %s", sortDict(indexMap)) else: logger.info("Action ordering will use 'dependency' order mode.") graph = DirectedGraph("dependencies") graph.createVertex("rebuild") graph.createVertex("validate") graph.createVertex("initialize") graph.createVertex("collect") graph.createVertex("stage") graph.createVertex("store") graph.createVertex("purge") for action in extensions.actions: graph.createVertex(action.name) graph.createEdge("collect", "stage") # Collect must run before stage, store or purge graph.createEdge("collect", "store") graph.createEdge("collect", "purge") graph.createEdge("stage", "store") # Stage must run before store or purge graph.createEdge("stage", "purge") graph.createEdge("store", "purge") # Store must run before purge for action in extensions.actions: if action.dependencies.beforeList is not None: for vertex in action.dependencies.beforeList: try: graph.createEdge(action.name, vertex) # actions that this action must be run before except ValueError: logger.error("Dependency [%s] on extension [%s] is unknown.", vertex, action.name) raise ValueError("Unable to determine proper action order due to invalid dependency.") if action.dependencies.afterList is not None: for vertex in action.dependencies.afterList: try: graph.createEdge(vertex, action.name) # actions that this action must be run after except ValueError: logger.error("Dependency [%s] on extension [%s] is unknown.", vertex, action.name) raise ValueError("Unable to determine proper action order due to invalid dependency.") try: ordering = graph.topologicalSort() indexMap = dict([(ordering[i], i + 1) for i in range(0, len(ordering))]) logger.info("Action order will be: %s", ordering) except ValueError: logger.error("Unable to determine proper action order due to dependency recursion.") logger.error("Extensions configuration is invalid (check for loops).") raise ValueError("Unable to determine proper action order due to dependency recursion.") return indexMap @staticmethod def _buildActionMap(managed, local, extensionNames, functionMap, indexMap, preHookMap, postHookMap, peerMap): """ Builds a mapping from action name to list of action items. We build either ``_ActionItem`` or ``_ManagedActionItem`` objects here. In most cases, the mapping from action name to ``_ActionItem`` is 1:1. The exception is the "all" action, which is a special case. However, a list is returned in all cases, just for consistency later. Each ``_ActionItem`` will be created with a proper function reference and index value for execution ordering. The mapping from action name to ``_ManagedActionItem`` is always 1:1. Each managed action item contains a list of peers which the action should be executed. Args: managed: Whether to include managed actions in the set local: Whether to include local actions in the set extensionNames: List of valid extended action names functionMap: Dictionary mapping action name to Python function indexMap: Dictionary mapping action name to integer execution index preHookMap: Dictionary mapping action name to pre hooks (if any) for the action postHookMap: Dictionary mapping action name to post hooks (if any) for the action peerMap: Dictionary mapping action name to list of remote peers on which to execute the action Returns: Dictionary mapping action name to list of ``_ActionItem`` objects """ actionMap = {} for name in extensionNames + VALID_ACTIONS: if name != "all": # do this one later function = functionMap[name] index = indexMap[name] actionMap[name] = [] if local: (preHooks, postHooks) = _ActionSet._deriveHooks(name, preHookMap, postHookMap) actionMap[name].append(_ActionItem(index, name, preHooks, postHooks, function)) if managed: if name in peerMap: actionMap[name].append(_ManagedActionItem(index, name, peerMap[name])) actionMap["all"] = actionMap["collect"] + actionMap["stage"] + actionMap["store"] + actionMap["purge"] return actionMap @staticmethod def _buildPeerMap(options, peers): """ Build a mapping from action name to list of remote peers. There will be one entry in the mapping for each managed action. If there are no managed peers, the mapping will be empty. Only managed actions will be listed in the mapping. Args: options: Option configuration (i.e. config.options) peers: Peers configuration (i.e. config.peers) """ peerMap = {} if peers is not None: if peers.remotePeers is not None: for peer in peers.remotePeers: if peer.managed: remoteUser = _ActionSet._getRemoteUser(options, peer) rshCommand = _ActionSet._getRshCommand(options, peer) cbackCommand = _ActionSet._getCbackCommand(options, peer) managedActions = _ActionSet._getManagedActions(options, peer) remotePeer = RemotePeer( peer.name, None, options.workingDir, remoteUser, None, options.backupUser, rshCommand, cbackCommand ) if managedActions is not None: for managedAction in managedActions: if managedAction in peerMap: if remotePeer not in peerMap[managedAction]: peerMap[managedAction].append(remotePeer) else: peerMap[managedAction] = [remotePeer] return peerMap @staticmethod def _deriveHooks(action, preHookDict, postHookDict): """ Derive pre- and post-action hooks, if any, associated with named action. Args: action: Name of action to look up preHookDict: Dictionary mapping pre-action hooks to action name postHookDict: Dictionary mapping post-action hooks to action name @return Tuple (preHooks, postHooks) per mapping, with None values if there is no hook """ preHooks = None postHooks = None if action in preHookDict: preHooks = preHookDict[action] if action in postHookDict: postHooks = postHookDict[action] return (preHooks, postHooks) @staticmethod def _validateActions(actions, extensionNames): """ Validate that the set of specified actions is sensible. Any specified action must either be a built-in action or must be among the extended actions defined in configuration. The actions from within :any:`NONCOMBINE_ACTIONS` may not be combined with other actions. Args: actions: Names of actions specified on the command-line extensionNames: Names of extensions specified in configuration Raises: ValueError: If one or more configured actions are not valid """ if actions is None or actions == []: raise ValueError("No actions specified.") for action in actions: if action not in VALID_ACTIONS and action not in extensionNames: raise ValueError("Action [%s] is not a valid action or extended action." % action) for action in NONCOMBINE_ACTIONS: if action in actions and actions != [action]: raise ValueError("Action [%s] may not be combined with other actions." % action) @staticmethod def _buildActionSet(actions, actionMap): """ Build set of actions to be executed. The set of actions is built in the proper order, so ``executeActions`` can spin through the set without thinking about it. Since we've already validated that the set of actions is sensible, we don't take any precautions here to make sure things are combined properly. If the action is listed, it will be "scheduled" for execution. Args: actions: Names of actions specified on the command-line actionMap: Dictionary mapping action name to ``_ActionItem`` object Returns: Set of action items in proper order """ actionSet = [] for action in actions: actionSet.extend(actionMap[action]) actionSet.sort() # sort the actions in order by index return actionSet def executeActions(self, configPath, options, config): """ Executes all actions and extended actions, in the proper order. Each action (whether built-in or extension) is executed in an identical manner. The built-in actions will use only the options and config values. We also pass in the config path so that extension modules can re-parse configuration if they want to, to add in extra information. Args: configPath: Path to configuration file on disk options: Command-line options to be passed to action functions config: Parsed configuration to be passed to action functions Raises: Exception: If there is a problem executing the actions """ logger.debug("Executing local actions.") for actionItem in self.actionSet: actionItem.executeAction(configPath, options, config) @staticmethod def _getRemoteUser(options, remotePeer): """ Gets the remote user associated with a remote peer. Use peer's if possible, otherwise take from options section. Args: options: OptionsConfig object, as from config.options remotePeer: Configuration-style remote peer object Returns: Name of remote user associated with remote peer """ if remotePeer.remoteUser is None: return options.backupUser return remotePeer.remoteUser @staticmethod def _getRshCommand(options, remotePeer): """ Gets the RSH command associated with a remote peer. Use peer's if possible, otherwise take from options section. Args: options: OptionsConfig object, as from config.options remotePeer: Configuration-style remote peer object Returns: RSH command associated with remote peer """ if remotePeer.rshCommand is None: return options.rshCommand return remotePeer.rshCommand @staticmethod def _getCbackCommand(options, remotePeer): """ Gets the cback command associated with a remote peer. Use peer's if possible, otherwise take from options section. Args: options: OptionsConfig object, as from config.options remotePeer: Configuration-style remote peer object Returns: cback command associated with remote peer """ if remotePeer.cbackCommand is None: return options.cbackCommand return remotePeer.cbackCommand @staticmethod def _getManagedActions(options, remotePeer): """ Gets the managed actions list associated with a remote peer. Use peer's if possible, otherwise take from options section. Args: options: OptionsConfig object, as from config.options remotePeer: Configuration-style remote peer object Returns: Set of managed actions associated with remote peer """ if remotePeer.managedActions is None: return options.managedActions return remotePeer.managedActions ####################################################################### # Utility functions ####################################################################### #################### # _usage() function #################### def _usage(fd=sys.stderr): """ Prints usage information for the cback3 script. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write(" Usage: cback3 [switches] action(s)\n") fd.write("\n") fd.write(" The following switches are accepted:\n") fd.write("\n") fd.write(" -h, --help Display this usage/help listing\n") fd.write(" -V, --version Display version information\n") fd.write(" -b, --verbose Print verbose output as well as logging to disk\n") fd.write(" -q, --quiet Run quietly (display no output to the screen)\n") fd.write(" -c, --config Path to config file (default: %s)\n" % DEFAULT_CONFIG) fd.write(" -f, --full Perform a full backup, regardless of configuration\n") fd.write(" -M, --managed Include managed clients when executing actions\n") fd.write(" -N, --managed-only Include ONLY managed clients when executing actions\n") fd.write(" -l, --logfile Path to logfile (default: %s)\n" % DEFAULT_LOGFILE) fd.write( " -o, --owner Logfile ownership, user:group (default: %s:%s)\n" % (DEFAULT_OWNERSHIP[0], DEFAULT_OWNERSHIP[1]) ) fd.write(" -m, --mode Octal logfile permissions mode (default: %o)\n" % DEFAULT_MODE) fd.write(" -O, --output Record some sub-command (i.e. cdrecord) output to the log\n") fd.write(" -d, --debug Write debugging information to the log (implies --output)\n") fd.write( " -s, --stack Dump a Python stack trace instead of swallowing exceptions\n" ) # exactly 80 characters in width! fd.write(" -D, --diagnostics Print runtime diagnostics to the screen and exit\n") fd.write("\n") fd.write(" The following actions may be specified:\n") fd.write("\n") fd.write(" all Take all normal actions (collect, stage, store, purge)\n") fd.write(" collect Take the collect action\n") fd.write(" stage Take the stage action\n") fd.write(" store Take the store action\n") fd.write(" purge Take the purge action\n") fd.write(' rebuild Rebuild "this week\'s" disc if possible\n') fd.write(" validate Validate configuration only\n") fd.write(" initialize Initialize media for use with Cedar Backup\n") fd.write("\n") fd.write(" You may also specify extended actions that have been defined in\n") fd.write(" configuration.\n") fd.write("\n") fd.write(" You must specify at least one action to take. More than one of\n") fd.write(' the "collect", "stage", "store" or "purge" actions and/or\n') fd.write(" extended actions may be specified in any arbitrary order; they\n") fd.write(' will be executed in a sensible order. The "all", "rebuild",\n') fd.write(' "validate", and "initialize" actions may not be combined with\n') fd.write(" other actions.\n") fd.write("\n") ###################### # _version() function ###################### def _version(fd=sys.stdout): """ Prints version information for the cback3 script. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write(" Cedar Backup version %s.\n" % VERSION) fd.write("\n") fd.write(" Copyright (c) %s <%s>.\n" % (AUTHOR, EMAIL)) fd.write(" See NOTICE for a list of included code and other contributors.\n") fd.write(" This is free software; there is NO warranty. See the\n") fd.write(" GNU General Public License version 2 for copying conditions.\n") fd.write("\n") fd.write(" Use the --help option for usage information.\n") fd.write("\n") ########################## # _diagnostics() function ########################## def _diagnostics(fd=sys.stdout): """ Prints runtime diagnostics information. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write("Diagnostics:\n") fd.write("\n") Diagnostics().printDiagnostics(fd=fd, prefix=" ") fd.write("\n") ########################## # setupLogging() function ########################## def setupLogging(options): """ Set up logging based on command-line options. There are two kinds of logging: flow logging and output logging. Output logging contains information about system commands executed by Cedar Backup, for instance the calls to ``mkisofs`` or ``mount``, etc. Flow logging contains error and informational messages used to understand program flow. Flow log messages and output log messages are written to two different loggers target (``CedarBackup3.log`` and ``CedarBackup3.output``). Flow log messages are written at the ERROR, INFO and DEBUG log levels, while output log messages are generally only written at the INFO log level. By default, output logging is disabled. When the ``options.output`` or ``options.debug`` flags are set, output logging will be written to the configured logfile. Output logging is never written to the screen. By default, flow logging is enabled at the ERROR level to the screen and at the INFO level to the configured logfile. If the ``options.quiet`` flag is set, flow logging is enabled at the INFO level to the configured logfile only (i.e. no output will be sent to the screen). If the ``options.verbose`` flag is set, flow logging is enabled at the INFO level to both the screen and the configured logfile. If the ``options.debug`` flag is set, flow logging is enabled at the DEBUG level to both the screen and the configured logfile. Args: options (:any:`Options` object): Command-line options Returns: Path to logfile on disk """ logfile = _setupLogfile(options) _setupFlowLogging(logfile, options) _setupOutputLogging(logfile, options) return logfile def _setupLogfile(options): """ Sets up and creates logfile as needed. If the logfile already exists on disk, it will be left as-is, under the assumption that it was created with appropriate ownership and permissions. If the logfile does not exist on disk, it will be created as an empty file. Ownership and permissions will remain at their defaults unless user/group and/or mode are set in the options. We ignore errors setting the indicated user and group. *Note:* This function is vulnerable to a race condition. If the log file does not exist when the function is run, it will attempt to create the file as safely as possible (using ``O_CREAT``). If two processes attempt to create the file at the same time, then one of them will fail. In practice, this shouldn't really be a problem, but it might happen occassionally if two instances of cback3 run concurrently or if cback3 collides with logrotate or something. Args: options: Command-line options Returns: Path to logfile on disk """ if options.logfile is None: logfile = DEFAULT_LOGFILE else: logfile = options.logfile if not os.path.exists(logfile): mode = DEFAULT_MODE if options.mode is None else options.mode orig = os.umask(0) # Per os.open(), "When computing mode, the current umask value is first masked out" try: fd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND, mode) with os.fdopen(fd, "a+") as f: f.write("") finally: os.umask(orig) try: if options.owner is None or len(options.owner) < 2: (uid, gid) = getUidGid(DEFAULT_OWNERSHIP[0], DEFAULT_OWNERSHIP[1]) else: (uid, gid) = getUidGid(options.owner[0], options.owner[1]) if sys.platform != "win32": os.chown(logfile, uid, gid) # pylint: disable=no-member except: pass return logfile def _setupFlowLogging(logfile, options): """ Sets up flow logging. Args: logfile: Path to logfile on disk options: Command-line options """ flowLogger = logging.getLogger("CedarBackup3.log") flowLogger.setLevel(logging.DEBUG) # let the logger see all messages _setupDiskFlowLogging(flowLogger, logfile, options) _setupScreenFlowLogging(flowLogger, options) def _setupOutputLogging(logfile, options): """ Sets up command output logging. Args: logfile: Path to logfile on disk options: Command-line options """ outputLogger = logging.getLogger("CedarBackup3.output") outputLogger.setLevel(logging.DEBUG) # let the logger see all messages _setupDiskOutputLogging(outputLogger, logfile, options) def _setupDiskFlowLogging(flowLogger, logfile, options): """ Sets up on-disk flow logging. Args: flowLogger: Python flow logger object logfile: Path to logfile on disk options: Command-line options """ formatter = logging.Formatter(fmt=DISK_LOG_FORMAT, datefmt=DATE_FORMAT) handler = logging.FileHandler(logfile, mode="a", encoding="utf-8") handler.setFormatter(formatter) if options.debug: handler.setLevel(logging.DEBUG) else: handler.setLevel(logging.INFO) flowLogger.addHandler(handler) def _setupScreenFlowLogging(flowLogger, options): """ Sets up on-screen flow logging. Args: flowLogger: Python flow logger object options: Command-line options """ formatter = logging.Formatter(fmt=SCREEN_LOG_FORMAT) handler = logging.StreamHandler(SCREEN_LOG_STREAM) handler.setFormatter(formatter) if options.quiet: handler.setLevel(logging.CRITICAL) # effectively turn it off elif options.verbose: if options.debug: handler.setLevel(logging.DEBUG) else: handler.setLevel(logging.INFO) else: handler.setLevel(logging.ERROR) flowLogger.addHandler(handler) def _setupDiskOutputLogging(outputLogger, logfile, options): """ Sets up on-disk command output logging. Args: outputLogger: Python command output logger object logfile: Path to logfile on disk options: Command-line options """ formatter = logging.Formatter(fmt=DISK_OUTPUT_FORMAT, datefmt=DATE_FORMAT) handler = logging.FileHandler(logfile, mode="a", encoding="utf-8") handler.setFormatter(formatter) if options.debug or options.output: handler.setLevel(logging.DEBUG) else: handler.setLevel(logging.CRITICAL) # effectively turn it off outputLogger.addHandler(handler) ############################### # setupPathResolver() function ############################### def setupPathResolver(config): """ Set up the path resolver singleton based on configuration. Cedar Backup's path resolver is implemented in terms of a singleton, the :any:`PathResolverSingleton` class. This function takes options configuration, converts it into the dictionary form needed by the singleton, and then initializes the singleton. After that, any function that needs to resolve the path of a command can use the singleton. Args: config (:any:`Config` object): Configuration """ mapping = {} if config.options.overrides is not None: for override in config.options.overrides: mapping[override.command] = override.absolutePath singleton = PathResolverSingleton() singleton.fill(mapping) ######################################################################### # Options class definition ######################################################################## @total_ordering class Options(object): ###################### # Class documentation ###################### """ Class representing command-line options for the cback3 script. The ``Options`` class is a Python object representation of the command-line options of the cback3 script. The object representation is two-way: a command line string or a list of command line arguments can be used to create an ``Options`` object, and then changes to the object can be propogated back to a list of command-line arguments or to a command-line string. An ``Options`` object can even be created from scratch programmatically (if you have a need for that). There are two main levels of validation in the ``Options`` class. The first is field-level validation. Field-level validation comes into play when a given field in an object is assigned to or updated. We use Python's ``property`` functionality to enforce specific validations on field values, and in some places we even use customized list classes to enforce validations on list members. You should expect to catch a ``ValueError`` exception when making assignments to fields if you are programmatically filling an object. The second level of validation is post-completion validation. Certain validations don't make sense until an object representation of options is fully "complete". We don't want these validations to apply all of the time, because it would make building up a valid object from scratch a real pain. For instance, we might have to do things in the right order to keep from throwing exceptions, etc. All of these post-completion validations are encapsulated in the :any:`Options.validate` method. This method can be called at any time by a client, and will always be called immediately after creating a ``Options`` object from a command line and before exporting a ``Options`` object back to a command line. This way, we get acceptable ease-of-use but we also don't accept or emit invalid command lines. *Note:* Lists within this class are "unordered" for equality comparisons. """ ############## # Constructor ############## def __init__(self, argumentList=None, argumentString=None, validate=True): """ Initializes an options object. If you initialize the object without passing either ``argumentList`` or ``argumentString``, the object will be empty and will be invalid until it is filled in properly. No reference to the original arguments is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. The argument list is assumed to be a list of arguments, not including the name of the command, something like ``sys.argv[1:]``. If you pass ``sys.argv`` instead, things are not going to work. The argument string will be parsed into an argument list by the :any:`util.splitCommandLine` function (see the documentation for that function for some important notes about its limitations). There is an assumption that the resulting list will be equivalent to ``sys.argv[1:]``, just like ``argumentList``. Unless the ``validate`` argument is ``False``, the :any:`Options.validate` method will be called (with its default arguments) after successfully parsing any passed-in command line. This validation ensures that appropriate actions, etc. have been specified. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in command line, so an exception might still be raised. *Note:* The command line format is specified by the ``_usage`` function. Call ``_usage`` to see a usage statement for the cback3 script. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid command line arguments. Args: argumentList (List of arguments, i.e. ``sys.argv``): Command line for a program argumentString (String, i.e. "cback3 --verbose stage store"): Command line for a program validate (Boolean true/false): Validate the command line after parsing it Raises: getopt.GetoptError: If the command-line arguments could not be parsed ValueError: If the command-line arguments are invalid """ self._help = False self._version = False self._verbose = False self._quiet = False self._config = None self._full = False self._managed = False self._managedOnly = False self._logfile = None self._owner = None self._mode = None self._output = False self._debug = False self._stacktrace = False self._diagnostics = False self._actions = None self.actions = [] # initialize to an empty list; remainder are OK if argumentList is not None and argumentString is not None: raise ValueError("Use either argumentList or argumentString, but not both.") if argumentString is not None: argumentList = splitCommandLine(argumentString) if argumentList is not None: self._parseArgumentList(argumentList) if validate: self.validate() ######################### # String representations ######################### def __repr__(self): """ Official string representation for class instance. """ return self.buildArgumentString(validate=False) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() ############################# # Standard comparison method ############################# def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.help != other.help: if self.help < other.help: return -1 else: return 1 if self.version != other.version: if self.version < other.version: return -1 else: return 1 if self.verbose != other.verbose: if self.verbose < other.verbose: return -1 else: return 1 if self.quiet != other.quiet: if self.quiet < other.quiet: return -1 else: return 1 if self.config != other.config: if self.config < other.config: return -1 else: return 1 if self.full != other.full: if self.full < other.full: return -1 else: return 1 if self.managed != other.managed: if self.managed < other.managed: return -1 else: return 1 if self.managedOnly != other.managedOnly: if self.managedOnly < other.managedOnly: return -1 else: return 1 if self.logfile != other.logfile: if str(self.logfile or "") < str(other.logfile or ""): return -1 else: return 1 if self.owner != other.owner: if str(self.owner or "") < str(other.owner or ""): return -1 else: return 1 if self.mode != other.mode: if int(self.mode or 0) < int(other.mode or 0): return -1 else: return 1 if self.output != other.output: if self.output < other.output: return -1 else: return 1 if self.debug != other.debug: if self.debug < other.debug: return -1 else: return 1 if self.stacktrace != other.stacktrace: if self.stacktrace < other.stacktrace: return -1 else: return 1 if self.diagnostics != other.diagnostics: if self.diagnostics < other.diagnostics: return -1 else: return 1 if self.actions != other.actions: if self.actions < other.actions: return -1 else: return 1 return 0 ############# # Properties ############# def _setHelp(self, value): """ Property target used to set the help flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._help = True else: self._help = False def _getHelp(self): """ Property target used to get the help flag. """ return self._help def _setVersion(self, value): """ Property target used to set the version flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._version = True else: self._version = False def _getVersion(self): """ Property target used to get the version flag. """ return self._version def _setVerbose(self, value): """ Property target used to set the verbose flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._verbose = True else: self._verbose = False def _getVerbose(self): """ Property target used to get the verbose flag. """ return self._verbose def _setQuiet(self, value): """ Property target used to set the quiet flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._quiet = True else: self._quiet = False def _getQuiet(self): """ Property target used to get the quiet flag. """ return self._quiet def _setConfig(self, value): """ Property target used to set the config parameter. """ if value is not None: if len(value) < 1: raise ValueError("The config parameter must be a non-empty string.") self._config = value def _getConfig(self): """ Property target used to get the config parameter. """ return self._config def _setFull(self, value): """ Property target used to set the full flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._full = True else: self._full = False def _getFull(self): """ Property target used to get the full flag. """ return self._full def _setManaged(self, value): """ Property target used to set the managed flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._managed = True else: self._managed = False def _getManaged(self): """ Property target used to get the managed flag. """ return self._managed def _setManagedOnly(self, value): """ Property target used to set the managedOnly flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._managedOnly = True else: self._managedOnly = False def _getManagedOnly(self): """ Property target used to get the managedOnly flag. """ return self._managedOnly def _setLogfile(self, value): """ Property target used to set the logfile parameter. Raises: ValueError: If the value cannot be encoded properly """ if value is not None: if len(value) < 1: raise ValueError("The logfile parameter must be a non-empty string.") self._logfile = encodePath(value) def _getLogfile(self): """ Property target used to get the logfile parameter. """ return self._logfile def _setOwner(self, value): """ Property target used to set the owner parameter. If not ``None``, the owner must be a ``(user,group)`` tuple or list. Strings (and inherited children of strings) are explicitly disallowed. The value will be normalized to a tuple. Raises: ValueError: If the value is not valid """ if value is None: self._owner = None else: if isinstance(value, str): raise ValueError("Must specify user and group tuple for owner parameter.") if len(value) != 2: raise ValueError("Must specify user and group tuple for owner parameter.") if len(value[0]) < 1 or len(value[1]) < 1: raise ValueError("User and group tuple values must be non-empty strings.") self._owner = (value[0], value[1]) def _getOwner(self): """ Property target used to get the owner parameter. The parameter is a tuple of ``(user, group)``. """ return self._owner def _setMode(self, value): """ Property target used to set the mode parameter. """ if value is None: self._mode = None else: try: if isinstance(value, str): value = int(value, 8) else: value = int(value) except TypeError: raise ValueError("Mode must be an octal integer >= 0, i.e. 644.") if value < 0: raise ValueError("Mode must be an octal integer >= 0. i.e. 644.") self._mode = value def _getMode(self): """ Property target used to get the mode parameter. """ return self._mode def _setOutput(self, value): """ Property target used to set the output flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._output = True else: self._output = False def _getOutput(self): """ Property target used to get the output flag. """ return self._output def _setDebug(self, value): """ Property target used to set the debug flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._debug = True else: self._debug = False def _getDebug(self): """ Property target used to get the debug flag. """ return self._debug def _setStacktrace(self, value): """ Property target used to set the stacktrace flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._stacktrace = True else: self._stacktrace = False def _getStacktrace(self): """ Property target used to get the stacktrace flag. """ return self._stacktrace def _setDiagnostics(self, value): """ Property target used to set the diagnostics flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._diagnostics = True else: self._diagnostics = False def _getDiagnostics(self): """ Property target used to get the diagnostics flag. """ return self._diagnostics def _setActions(self, value): """ Property target used to set the actions list. We don't restrict the contents of actions. They're validated somewhere else. Raises: ValueError: If the value is not valid """ if value is None: self._actions = None else: try: saved = self._actions self._actions = [] self._actions.extend(value) except Exception as e: self._actions = saved raise e def _getActions(self): """ Property target used to get the actions list. """ return self._actions help = property(_getHelp, _setHelp, None, "Command-line help (``-h,--help``) flag.") version = property(_getVersion, _setVersion, None, "Command-line version (``-V,--version``) flag.") verbose = property(_getVerbose, _setVerbose, None, "Command-line verbose (``-b,--verbose``) flag.") quiet = property(_getQuiet, _setQuiet, None, "Command-line quiet (``-q,--quiet``) flag.") config = property(_getConfig, _setConfig, None, "Command-line configuration file (``-c,--config``) parameter.") full = property(_getFull, _setFull, None, "Command-line full-backup (``-f,--full``) flag.") managed = property(_getManaged, _setManaged, None, "Command-line managed (``-M,--managed``) flag.") managedOnly = property(_getManagedOnly, _setManagedOnly, None, "Command-line managed-only (``-N,--managed-only``) flag.") logfile = property(_getLogfile, _setLogfile, None, "Command-line logfile (``-l,--logfile``) parameter.") owner = property(_getOwner, _setOwner, None, "Command-line owner (``-o,--owner``) parameter, as tuple ``(user,group)``.") mode = property(_getMode, _setMode, None, "Command-line mode (``-m,--mode``) parameter.") output = property(_getOutput, _setOutput, None, "Command-line output (``-O,--output``) flag.") debug = property(_getDebug, _setDebug, None, "Command-line debug (``-d,--debug``) flag.") stacktrace = property(_getStacktrace, _setStacktrace, None, "Command-line stacktrace (``-s,--stack``) flag.") diagnostics = property(_getDiagnostics, _setDiagnostics, None, "Command-line diagnostics (``-D,--diagnostics``) flag.") actions = property(_getActions, _setActions, None, "Command-line actions list.") ################## # Utility methods ################## def validate(self): """ Validates command-line options represented by the object. Unless ``--help`` or ``--version`` are supplied, at least one action must be specified. Other validations (as for allowed values for particular options) will be taken care of at assignment time by the properties functionality. *Note:* The command line format is specified by the ``_usage`` function. Call ``_usage`` to see a usage statement for the cback3 script. Raises: ValueError: If one of the validations fails """ if not self.help and not self.version and not self.diagnostics: if self.actions is None or len(self.actions) == 0: raise ValueError("At least one action must be specified.") if self.managed and self.managedOnly: raise ValueError("The --managed and --managed-only options may not be combined.") def buildArgumentList(self, validate=True): """ Extracts options into a list of command line arguments. The original order of the various arguments (if, indeed, the object was initialized with a command-line) is not preserved in this generated argument list. Besides that, the argument list is normalized to use the long option names (i.e. --version rather than -V). The resulting list will be suitable for passing back to the constructor in the ``argumentList`` parameter. Unlike :any:`buildArgumentString`, string arguments are not quoted here, because there is no need for it. Unless the ``validate`` parameter is ``False``, the :any:`Options.validate` method will be called (with its default arguments) against the options before extracting the command line. If the options are not valid, then an argument list will not be extracted. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to extract an invalid command line. Args: validate (Boolean true/false): Validate the options before extracting the command line Returns: List representation of command-line arguments Raises: ValueError: If options within the object are invalid """ if validate: self.validate() argumentList = [] if self._help: argumentList.append("--help") if self.version: argumentList.append("--version") if self.verbose: argumentList.append("--verbose") if self.quiet: argumentList.append("--quiet") if self.config is not None: argumentList.append("--config") argumentList.append(self.config) if self.full: argumentList.append("--full") if self.managed: argumentList.append("--managed") if self.managedOnly: argumentList.append("--managed-only") if self.logfile is not None: argumentList.append("--logfile") argumentList.append(self.logfile) if self.owner is not None: argumentList.append("--owner") argumentList.append("%s:%s" % (self.owner[0], self.owner[1])) if self.mode is not None: argumentList.append("--mode") argumentList.append("%o" % self.mode) if self.output: argumentList.append("--output") if self.debug: argumentList.append("--debug") if self.stacktrace: argumentList.append("--stack") if self.diagnostics: argumentList.append("--diagnostics") if self.actions is not None: for action in self.actions: argumentList.append(action) return argumentList def buildArgumentString(self, validate=True): """ Extracts options into a string of command-line arguments. The original order of the various arguments (if, indeed, the object was initialized with a command-line) is not preserved in this generated argument string. Besides that, the argument string is normalized to use the long option names (i.e. --version rather than -V) and to quote all string arguments with double quotes (``"``). The resulting string will be suitable for passing back to the constructor in the ``argumentString`` parameter. Unless the ``validate`` parameter is ``False``, the :any:`Options.validate` method will be called (with its default arguments) against the options before extracting the command line. If the options are not valid, then an argument string will not be extracted. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to extract an invalid command line. Args: validate (Boolean true/false): Validate the options before extracting the command line Returns: String representation of command-line arguments Raises: ValueError: If options within the object are invalid """ if validate: self.validate() argumentString = "" if self._help: argumentString += "--help " if self.version: argumentString += "--version " if self.verbose: argumentString += "--verbose " if self.quiet: argumentString += "--quiet " if self.config is not None: argumentString += '--config "%s" ' % self.config if self.full: argumentString += "--full " if self.managed: argumentString += "--managed " if self.managedOnly: argumentString += "--managed-only " if self.logfile is not None: argumentString += '--logfile "%s" ' % self.logfile if self.owner is not None: argumentString += '--owner "%s:%s" ' % (self.owner[0], self.owner[1]) if self.mode is not None: argumentString += "--mode %o " % self.mode if self.output: argumentString += "--output " if self.debug: argumentString += "--debug " if self.stacktrace: argumentString += "--stack " if self.diagnostics: argumentString += "--diagnostics " if self.actions is not None: for action in self.actions: argumentString += '"%s" ' % action return argumentString def _parseArgumentList(self, argumentList): """ Internal method to parse a list of command-line arguments. Most of the validation we do here has to do with whether the arguments can be parsed and whether any values which exist are valid. We don't do any validation as to whether required elements exist or whether elements exist in the proper combination (instead, that's the job of the :any:`validate` method). For any of the options which supply parameters, if the option is duplicated with long and short switches (i.e. ``-l`` and a ``--logfile``) then the long switch is used. If the same option is duplicated with the same switch (long or short), then the last entry on the command line is used. Args: argumentList (List of arguments to a command, i.e. ``sys.argv[1:]``): List of arguments to a command Raises: ValueError: If the argument list cannot be successfully parsed """ switches = {} opts, self.actions = getopt.getopt(argumentList, SHORT_SWITCHES, LONG_SWITCHES) for o, a in opts: # push the switches into a hash switches[o] = a if "-h" in switches or "--help" in switches: self.help = True if "-V" in switches or "--version" in switches: self.version = True if "-b" in switches or "--verbose" in switches: self.verbose = True if "-q" in switches or "--quiet" in switches: self.quiet = True if "-c" in switches: self.config = switches["-c"] if "--config" in switches: self.config = switches["--config"] if "-f" in switches or "--full" in switches: self.full = True if "-M" in switches or "--managed" in switches: self.managed = True if "-N" in switches or "--managed-only" in switches: self.managedOnly = True if "-l" in switches: self.logfile = switches["-l"] if "--logfile" in switches: self.logfile = switches["--logfile"] if "-o" in switches: self.owner = switches["-o"].split(":", 1) if "--owner" in switches: self.owner = switches["--owner"].split(":", 1) if "-m" in switches: self.mode = switches["-m"] if "--mode" in switches: self.mode = switches["--mode"] if "-O" in switches or "--output" in switches: self.output = True if "-d" in switches or "--debug" in switches: self.debug = True if "-s" in switches or "--stack" in switches: self.stacktrace = True if "-D" in switches or "--diagnostics" in switches: self.diagnostics = True ######################################################################### # Main routine ######################################################################## if __name__ == "__main__": result = cli() sys.exit(result) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/config.py0000644000000000000000000075103214567004737016021 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides configuration-related objects. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides configuration-related objects. Summary ======= Cedar Backup stores all of its configuration in an XML document typically called ``cback3.conf``. The standard location for this document is in ``/etc``, but users can specify a different location if they want to. The ``Config`` class is a Python object representation of a Cedar Backup XML configuration file. The representation is two-way: XML data can be used to create a ``Config`` object, and then changes to the object can be propogated back to disk. A ``Config`` object can even be used to create a configuration file from scratch programmatically. The ``Config`` class is intended to be the only Python-language interface to Cedar Backup configuration on disk. Cedar Backup will use the class as its internal representation of configuration, and applications external to Cedar Backup itself (such as a hypothetical third-party configuration tool written in Python or a third party extension module) should also use the class when they need to read and write configuration files. Backwards Compatibility ======================= The configuration file format has changed between Cedar Backup 1.x and Cedar Backup 2.x. Any Cedar Backup 1.x configuration file is also a valid Cedar Backup 2.x configuration file. However, it doesn't work to go the other direction, as the 2.x configuration files contains additional configuration is not accepted by older versions of the software. XML Configuration Structure =========================== A ``Config`` object can either be created "empty", or can be created based on XML input (either in the form of a string or read in from a file on disk). Generally speaking, the XML input *must* result in a ``Config`` object which passes the validations laid out below in the *Validation* section. An XML configuration file is composed of seven sections: - *reference*: specifies reference information about the file (author, revision, etc) - *extensions*: specifies mappings to Cedar Backup extensions (external code) - *options*: specifies global configuration options - *peers*: specifies the set of peers in a master's backup pool - *collect*: specifies configuration related to the collect action - *stage*: specifies configuration related to the stage action - *store*: specifies configuration related to the store action - *purge*: specifies configuration related to the purge action Each section is represented by an class in this module, and then the overall ``Config`` class is a composition of the various other classes. Any configuration section that is missing in the XML document (or has not been filled into an "empty" document) will just be set to ``None`` in the object representation. The same goes for individual fields within each configuration section. Keep in mind that the document might not be completely valid if some sections or fields aren't filled in - but that won't matter until validation takes place (see the *Validation* section below). Unicode vs. String Data ======================= By default, all string data that comes out of XML documents in Python is unicode data (i.e. ``u"whatever"``). This is fine for many things, but when it comes to filesystem paths, it can cause us some problems. We really want strings to be encoded in the filesystem encoding rather than being unicode. So, most elements in configuration which represent filesystem paths are coverted to plain strings using :any:`util.encodePath`. The main exception is the various ``absoluteExcludePath`` and ``relativeExcludePath`` lists. These are *not* converted, because they are generally only used for filtering, not for filesystem operations. Validation ========== There are two main levels of validation in the ``Config`` class and its children. The first is field-level validation. Field-level validation comes into play when a given field in an object is assigned to or updated. We use Python's ``property`` functionality to enforce specific validations on field values, and in some places we even use customized list classes to enforce validations on list members. You should expect to catch a ``ValueError`` exception when making assignments to configuration class fields. The second level of validation is post-completion validation. Certain validations don't make sense until a document is fully "complete". We don't want these validations to apply all of the time, because it would make building up a document from scratch a real pain. For instance, we might have to do things in the right order to keep from throwing exceptions, etc. All of these post-completion validations are encapsulated in the :any:`Config.validate` method. This method can be called at any time by a client, and will always be called immediately after creating a ``Config`` object from XML data and before exporting a ``Config`` object to XML. This way, we get decent ease-of-use but we also don't accept or emit invalid configuration files. The :any:`Config.validate` implementation actually takes two passes to completely validate a configuration document. The first pass at validation is to ensure that the proper sections are filled into the document. There are default requirements, but the caller has the opportunity to override these defaults. The second pass at validation ensures that any filled-in section contains valid data. Any section which is not set to ``None`` is validated according to the rules for that section (see below). *Reference Validations* No validations. *Extensions Validations* The list of actions may be either ``None`` or an empty list ``[]`` if desired. Each extended action must include a name, a module and a function. Then, an extended action must include either an index or dependency information. Which one is required depends on which order mode is configured. *Options Validations* All fields must be filled in except the rsh command. The rcp and rsh commands are used as default values for all remote peers. Remote peers can also rely on the backup user as the default remote user name if they choose. *Peers Validations* Local peers must be completely filled in, including both name and collect directory. Remote peers must also fill in the name and collect directory, but can leave the remote user and rcp command unset. In this case, the remote user is assumed to match the backup user from the options section and rcp command is taken directly from the options section. *Collect Validations* The target directory must be filled in. The collect mode, archive mode and ignore file are all optional. The list of absolute paths to exclude and patterns to exclude may be either ``None`` or an empty list ``[]`` if desired. Each collect directory entry must contain an absolute path to collect, and then must either be able to take collect mode, archive mode and ignore file configuration from the parent ``CollectConfig`` object, or must set each value on its own. The list of absolute paths to exclude, relative paths to exclude and patterns to exclude may be either ``None`` or an empty list ``[]`` if desired. Any list of absolute paths to exclude or patterns to exclude will be combined with the same list in the ``CollectConfig`` object to make the complete list for a given directory. *Stage Validations* The target directory must be filled in. There must be at least one peer (remote or local) between the two lists of peers. A list with no entries can be either ``None`` or an empty list ``[]`` if desired. If a set of peers is provided, this configuration completely overrides configuration in the peers configuration section, and the same validations apply. *Store Validations* The device type and drive speed are optional, and all other values are required (missing booleans will be set to defaults, which is OK). The image writer functionality in the ``writer`` module is supposed to be able to handle a device speed of ``None``. Any caller which needs a "real" (non-``None``) value for the device type can use ``DEFAULT_DEVICE_TYPE``, which is guaranteed to be sensible. *Purge Validations* The list of purge directories may be either ``None`` or an empty list ``[]`` if desired. All purge directories must contain a path and a retain days value. Module Attributes ================= Attributes: DEFAULT_DEVICE_TYPE: The default device type DEFAULT_MEDIA_TYPE: The default media type VALID_DEVICE_TYPES: List of valid device types VALID_MEDIA_TYPES: List of valid media types VALID_COLLECT_MODES: List of valid collect modes VALID_COMPRESS_MODES: List of valid compress modes VALID_ARCHIVE_MODES: List of valid archive modes VALID_ORDER_MODES: List of valid extension order modes :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import re from functools import total_ordering from CedarBackup3.util import ( UNIT_BYTES, UNIT_GBYTES, UNIT_KBYTES, UNIT_MBYTES, AbsolutePathList, ObjectTypeList, RegexList, RegexMatchList, UnorderedList, checkUnique, convertSize, displayBytes, encodePath, parseCommaSeparatedString, ) from CedarBackup3.writers.util import validateDriveSpeed, validateScsiId from CedarBackup3.xmlutil import ( addBooleanNode, addContainerNode, addIntegerNode, addStringNode, createInputDom, createOutputDom, isElement, readBoolean, readChildren, readFirstChild, readInteger, readString, readStringList, serializeDom, ) ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.config") DEFAULT_DEVICE_TYPE = "cdwriter" DEFAULT_MEDIA_TYPE = "cdrw-74" VALID_DEVICE_TYPES = ["cdwriter", "dvdwriter"] VALID_CD_MEDIA_TYPES = ["cdr-74", "cdrw-74", "cdr-80", "cdrw-80"] VALID_DVD_MEDIA_TYPES = ["dvd+r", "dvd+rw"] VALID_MEDIA_TYPES = VALID_CD_MEDIA_TYPES + VALID_DVD_MEDIA_TYPES VALID_COLLECT_MODES = ["daily", "weekly", "incr"] VALID_ARCHIVE_MODES = ["tar", "targz", "tarbz2"] VALID_COMPRESS_MODES = ["none", "gzip", "bzip2"] VALID_ORDER_MODES = ["index", "dependency"] VALID_BLANK_MODES = ["daily", "weekly"] VALID_BYTE_UNITS = [UNIT_BYTES, UNIT_KBYTES, UNIT_MBYTES, UNIT_GBYTES] VALID_FAILURE_MODES = ["none", "all", "daily", "weekly"] REWRITABLE_MEDIA_TYPES = ["cdrw-74", "cdrw-80", "dvd+rw"] ACTION_NAME_REGEX = r"^[a-z0-9]*$" ######################################################################## # ByteQuantity class definition ######################################################################## @total_ordering class ByteQuantity(object): """ Class representing a byte quantity. A byte quantity has both a quantity and a byte-related unit. Units are maintained using the constants from util.py. If no units are provided, ``UNIT_BYTES`` is assumed. The quantity is maintained internally as a string so that issues of precision can be avoided. It really isn't possible to store a floating point number here while being able to losslessly translate back and forth between XML and object representations. (Perhaps the Python 2.4 Decimal class would have been an option, but I originally wanted to stay compatible with Python 2.3.) Even though the quantity is maintained as a string, the string must be in a valid floating point positive number. Technically, any floating point string format supported by Python is allowble. However, it does not make sense to have a negative quantity of bytes in this context. """ def __init__(self, quantity=None, units=None): """ Constructor for the ``ByteQuantity`` class. Args: quantity: Quantity of bytes, something interpretable as a float units: Unit of bytes, one of VALID_BYTE_UNITS Raises: ValueError: If one of the values is invalid """ self._quantity = None self._units = None self.quantity = quantity self.units = units def __repr__(self): """ Official string representation for class instance. """ return "ByteQuantity(%s, %s)" % (self.quantity, self.units) def __str__(self): """ Informal string representation for class instance. """ return "%s" % displayBytes(self.bytes) def __eq__(self, other): """Equals operator, implemented in terms of Python 2-style compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of Python 2-style compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of Python 2-style compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Python 2-style comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 elif isinstance(other, ByteQuantity): if self.bytes != other.bytes: if self.bytes < other.bytes: return -1 else: return 1 return 0 else: return self.__cmp__(ByteQuantity(other, UNIT_BYTES)) # will fail if other can't be coverted to float def _setQuantity(self, value): """ Property target used to set the quantity The value must be interpretable as a float if it is not None Raises: ValueError: If the value is an empty string ValueError: If the value is not a valid floating point number ValueError: If the value is less than zero """ if value is None: self._quantity = None else: try: floatValue = float(value) # allow integer, float, string, etc. except: raise ValueError("Quantity must be interpretable as a float") if floatValue < 0.0: raise ValueError("Quantity cannot be negative.") self._quantity = str(value) # keep around string def _getQuantity(self): """ Property target used to get the quantity. """ return self._quantity def _setUnits(self, value): """ Property target used to set the units value. If not ``None``, the units value must be one of the values in :any:`VALID_BYTE_UNITS`. Raises: ValueError: If the value is not valid """ if value is None: self._units = UNIT_BYTES else: if value not in VALID_BYTE_UNITS: raise ValueError("Units value must be one of %s." % VALID_BYTE_UNITS) self._units = value def _getUnits(self): """ Property target used to get the units value. """ return self._units def _getBytes(self): """ Property target used to return the byte quantity as a floating point number. If there is no quantity set, then a value of 0.0 is returned. """ if self.quantity is not None and self.units is not None: return convertSize(self.quantity, self.units, UNIT_BYTES) return 0.0 quantity = property(_getQuantity, _setQuantity, None, doc="Byte quantity, as a string") units = property(_getUnits, _setUnits, None, doc="Units for byte quantity, for instance UNIT_BYTES") bytes = property(_getBytes, None, None, doc="Byte quantity, as a floating point number.") ######################################################################## # ActionDependencies class definition ######################################################################## @total_ordering class ActionDependencies(object): """ Class representing dependencies associated with an extended action. Execution ordering for extended actions is done in one of two ways: either by using index values (lower index gets run first) or by having the extended action specify dependencies in terms of other named actions. This class encapsulates the dependency information for an extended action. The following restrictions exist on data in this class: - Any action name must be a non-empty string matching ``ACTION_NAME_REGEX`` """ def __init__(self, beforeList=None, afterList=None): """ Constructor for the ``ActionDependencies`` class. Args: beforeList: List of named actions that this action must be run before afterList: List of named actions that this action must be run after Raises: ValueError: If one of the values is invalid """ self._beforeList = None self._afterList = None self.beforeList = beforeList self.afterList = afterList def __repr__(self): """ Official string representation for class instance. """ return "ActionDependencies(%s, %s)" % (self.beforeList, self.afterList) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.beforeList != other.beforeList: if self.beforeList < other.beforeList: return -1 else: return 1 if self.afterList != other.afterList: if self.afterList < other.afterList: return -1 else: return 1 return 0 def _setBeforeList(self, value): """ Property target used to set the "run before" list. Either the value must be ``None`` or each element must be a string matching ACTION_NAME_REGEX. Raises: ValueError: If the value does not match the regular expression """ if value is None: self._beforeList = None else: try: saved = self._beforeList self._beforeList = RegexMatchList(ACTION_NAME_REGEX, emptyAllowed=False, prefix="Action name") self._beforeList.extend(value) except Exception as e: self._beforeList = saved raise e def _getBeforeList(self): """ Property target used to get the "run before" list. """ return self._beforeList def _setAfterList(self, value): """ Property target used to set the "run after" list. Either the value must be ``None`` or each element must be a string matching ACTION_NAME_REGEX. Raises: ValueError: If the value does not match the regular expression """ if value is None: self._afterList = None else: try: saved = self._afterList self._afterList = RegexMatchList(ACTION_NAME_REGEX, emptyAllowed=False, prefix="Action name") self._afterList.extend(value) except Exception as e: self._afterList = saved raise e def _getAfterList(self): """ Property target used to get the "run after" list. """ return self._afterList beforeList = property(_getBeforeList, _setBeforeList, None, "List of named actions that this action must be run before.") afterList = property(_getAfterList, _setAfterList, None, "List of named actions that this action must be run after.") ######################################################################## # ActionHook class definition ######################################################################## @total_ordering class ActionHook(object): """ Class representing a hook associated with an action. A hook associated with an action is a shell command to be executed either before or after a named action is executed. The following restrictions exist on data in this class: - The action name must be a non-empty string matching ``ACTION_NAME_REGEX`` - The shell command must be a non-empty string. The internal ``before`` and ``after`` instance variables are always set to False in this parent class. """ def __init__(self, action=None, command=None): """ Constructor for the ``ActionHook`` class. Args: action: Action this hook is associated with command: Shell command to execute Raises: ValueError: If one of the values is invalid """ self._action = None self._command = None self._before = False self._after = False self.action = action self.command = command def __repr__(self): """ Official string representation for class instance. """ return "ActionHook(%s, %s, %s, %s)" % (self.action, self.command, self.before, self.after) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.action != other.action: if str(self.action or "") < str(other.action or ""): return -1 else: return 1 if self.command != other.command: if str(self.command or "") < str(other.command or ""): return -1 else: return 1 if self.before != other.before: if self.before < other.before: return -1 else: return 1 if self.after != other.after: if self.after < other.after: return -1 else: return 1 return 0 def _setAction(self, value): """ Property target used to set the action name. The value must be a non-empty string if it is not ``None``. It must also consist only of lower-case letters and digits. Raises: ValueError: If the value is an empty string """ pattern = re.compile(ACTION_NAME_REGEX) if value is not None: if len(value) < 1: raise ValueError("The action name must be a non-empty string.") if not pattern.search(value): raise ValueError("The action name must consist of only lower-case letters and digits.") self._action = value def _getAction(self): """ Property target used to get the action name. """ return self._action def _setCommand(self, value): """ Property target used to set the command. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The command must be a non-empty string.") self._command = value def _getCommand(self): """ Property target used to get the command. """ return self._command def _getBefore(self): """ Property target used to get the before flag. """ return self._before def _getAfter(self): """ Property target used to get the after flag. """ return self._after action = property(_getAction, _setAction, None, "Action this hook is associated with.") command = property(_getCommand, _setCommand, None, "Shell command to execute.") before = property(_getBefore, None, None, "Indicates whether command should be executed before action.") after = property(_getAfter, None, None, "Indicates whether command should be executed after action.") @total_ordering class PreActionHook(ActionHook): """ Class representing a pre-action hook associated with an action. A hook associated with an action is a shell command to be executed either before or after a named action is executed. In this case, a pre-action hook is executed before the named action. The following restrictions exist on data in this class: - The action name must be a non-empty string consisting of lower-case letters and digits. - The shell command must be a non-empty string. The internal ``before`` instance variable is always set to True in this class. """ def __init__(self, action=None, command=None): """ Constructor for the ``PreActionHook`` class. Args: action: Action this hook is associated with command: Shell command to execute Raises: ValueError: If one of the values is invalid """ ActionHook.__init__(self, action, command) self._before = True def __repr__(self): """ Official string representation for class instance. """ return "PreActionHook(%s, %s, %s, %s)" % (self.action, self.command, self.before, self.after) @total_ordering class PostActionHook(ActionHook): """ Class representing a pre-action hook associated with an action. A hook associated with an action is a shell command to be executed either before or after a named action is executed. In this case, a post-action hook is executed after the named action. The following restrictions exist on data in this class: - The action name must be a non-empty string consisting of lower-case letters and digits. - The shell command must be a non-empty string. The internal ``before`` instance variable is always set to True in this class. """ def __init__(self, action=None, command=None): """ Constructor for the ``PostActionHook`` class. Args: action: Action this hook is associated with command: Shell command to execute Raises: ValueError: If one of the values is invalid """ ActionHook.__init__(self, action, command) self._after = True def __repr__(self): """ Official string representation for class instance. """ return "PostActionHook(%s, %s, %s, %s)" % (self.action, self.command, self.before, self.after) ######################################################################## # BlankBehavior class definition ######################################################################## @total_ordering class BlankBehavior(object): """ Class representing optimized store-action media blanking behavior. The following restrictions exist on data in this class: - The blanking mode must be a one of the values in ``VALID_BLANK_MODES`` - The blanking factor must be a positive floating point number """ def __init__(self, blankMode=None, blankFactor=None): """ Constructor for the ``BlankBehavior`` class. Args: blankMode: Blanking mode blankFactor: Blanking factor Raises: ValueError: If one of the values is invalid """ self._blankMode = None self._blankFactor = None self.blankMode = blankMode self.blankFactor = blankFactor def __repr__(self): """ Official string representation for class instance. """ return "BlankBehavior(%s, %s)" % (self.blankMode, self.blankFactor) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.blankMode != other.blankMode: if str(self.blankMode or "") < str(other.blankMode or ""): return -1 else: return 1 if self.blankFactor != other.blankFactor: if float(self.blankFactor or 0.0) < float(other.blankFactor or 0.0): return -1 else: return 1 return 0 def _setBlankMode(self, value): """ Property target used to set the blanking mode. The value must be one of ``VALID_BLANK_MODES``. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_BLANK_MODES: raise ValueError("Blanking mode must be one of %s." % VALID_BLANK_MODES) self._blankMode = value def _getBlankMode(self): """ Property target used to get the blanking mode. """ return self._blankMode def _setBlankFactor(self, value): """ Property target used to set the blanking factor. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string ValueError: If the value is not a valid floating point number ValueError: If the value is less than zero """ if value is not None: if len(value) < 1: raise ValueError("Blanking factor must be a non-empty string.") floatValue = float(value) if floatValue < 0.0: raise ValueError("Blanking factor cannot be negative.") self._blankFactor = value # keep around string def _getBlankFactor(self): """ Property target used to get the blanking factor. """ return self._blankFactor blankMode = property(_getBlankMode, _setBlankMode, None, "Blanking mode") blankFactor = property(_getBlankFactor, _setBlankFactor, None, "Blanking factor") ######################################################################## # ExtendedAction class definition ######################################################################## @total_ordering class ExtendedAction(object): """ Class representing an extended action. Essentially, an extended action needs to allow the following to happen:: exec("from %s import %s" % (module, function)) exec("%s(action, configPath")" % function) The following restrictions exist on data in this class: - The action name must be a non-empty string consisting of lower-case letters and digits. - The module must be a non-empty string and a valid Python identifier. - The function must be an on-empty string and a valid Python identifier. - If set, the index must be a positive integer. - If set, the dependencies attribute must be an ``ActionDependencies`` object. """ def __init__(self, name=None, module=None, function=None, index=None, dependencies=None): """ Constructor for the ``ExtendedAction`` class. Args: name: Name of the extended action module: Name of the module containing the extended action function function: Name of the extended action function index: Index of action, used for execution ordering dependencies: Dependencies for action, used for execution ordering Raises: ValueError: If one of the values is invalid """ self._name = None self._module = None self._function = None self._index = None self._dependencies = None self.name = name self.module = module self.function = function self.index = index self.dependencies = dependencies def __repr__(self): """ Official string representation for class instance. """ return "ExtendedAction(%s, %s, %s, %s, %s)" % (self.name, self.module, self.function, self.index, self.dependencies) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.name != other.name: if str(self.name or "") < str(other.name or ""): return -1 else: return 1 if self.module != other.module: if str(self.module or "") < str(other.module or ""): return -1 else: return 1 if self.function != other.function: if str(self.function or "") < str(other.function or ""): return -1 else: return 1 if self.index != other.index: if int(self.index or 0) < int(other.index or 0): return -1 else: return 1 if self.dependencies != other.dependencies: if self.dependencies < other.dependencies: return -1 else: return 1 return 0 def _setName(self, value): """ Property target used to set the action name. The value must be a non-empty string if it is not ``None``. It must also consist only of lower-case letters and digits. Raises: ValueError: If the value is an empty string """ pattern = re.compile(ACTION_NAME_REGEX) if value is not None: if len(value) < 1: raise ValueError("The action name must be a non-empty string.") if not pattern.search(value): raise ValueError("The action name must consist of only lower-case letters and digits.") self._name = value def _getName(self): """ Property target used to get the action name. """ return self._name def _setModule(self, value): """ Property target used to set the module name. The value must be a non-empty string if it is not ``None``. It must also be a valid Python identifier. Raises: ValueError: If the value is an empty string """ pattern = re.compile(r"^([A-Za-z_][A-Za-z0-9_]*)(\.[A-Za-z_][A-Za-z0-9_]*)*$") if value is not None: if len(value) < 1: raise ValueError("The module name must be a non-empty string.") if not pattern.search(value): raise ValueError("The module name must be a valid Python identifier.") self._module = value def _getModule(self): """ Property target used to get the module name. """ return self._module def _setFunction(self, value): """ Property target used to set the function name. The value must be a non-empty string if it is not ``None``. It must also be a valid Python identifier. Raises: ValueError: If the value is an empty string """ pattern = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$") if value is not None: if len(value) < 1: raise ValueError("The function name must be a non-empty string.") if not pattern.search(value): raise ValueError("The function name must be a valid Python identifier.") self._function = value def _getFunction(self): """ Property target used to get the function name. """ return self._function def _setIndex(self, value): """ Property target used to set the action index. The value must be an integer >= 0. Raises: ValueError: If the value is not valid """ if value is None: self._index = None else: try: value = int(value) except TypeError: raise ValueError("Action index value must be an integer >= 0.") if value < 0: raise ValueError("Action index value must be an integer >= 0.") self._index = value def _getIndex(self): """ Property target used to get the action index. """ return self._index def _setDependencies(self, value): """ Property target used to set the action dependencies information. If not ``None``, the value must be a ``ActionDependecies`` object. Raises: ValueError: If the value is not a ``ActionDependencies`` object """ if value is None: self._dependencies = None else: if not isinstance(value, ActionDependencies): raise ValueError("Value must be a ``ActionDependencies`` object.") self._dependencies = value def _getDependencies(self): """ Property target used to get action dependencies information. """ return self._dependencies name = property(_getName, _setName, None, "Name of the extended action.") module = property(_getModule, _setModule, None, "Name of the module containing the extended action function.") function = property(_getFunction, _setFunction, None, "Name of the extended action function.") index = property(_getIndex, _setIndex, None, "Index of action, used for execution ordering.") dependencies = property(_getDependencies, _setDependencies, None, "Dependencies for action, used for execution ordering.") ######################################################################## # CommandOverride class definition ######################################################################## @total_ordering class CommandOverride(object): """ Class representing a piece of Cedar Backup command override configuration. The following restrictions exist on data in this class: - The absolute path must be absolute *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, command=None, absolutePath=None): """ Constructor for the ``CommandOverride`` class. Args: command: Name of command to be overridden absolutePath: Absolute path of the overrridden command Raises: ValueError: If one of the values is invalid """ self._command = None self._absolutePath = None self.command = command self.absolutePath = absolutePath def __repr__(self): """ Official string representation for class instance. """ return "CommandOverride(%s, %s)" % (self.command, self.absolutePath) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.command != other.command: if str(self.command or "") < str(other.command or ""): return -1 else: return 1 if self.absolutePath != other.absolutePath: if str(self.absolutePath or "") < str(other.absolutePath or ""): return -1 else: return 1 return 0 def _setCommand(self, value): """ Property target used to set the command. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The command must be a non-empty string.") self._command = value def _getCommand(self): """ Property target used to get the command. """ return self._command def _setAbsolutePath(self, value): """ Property target used to set the absolute path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Not an absolute path: [%s]" % value) self._absolutePath = encodePath(value) def _getAbsolutePath(self): """ Property target used to get the absolute path. """ return self._absolutePath command = property(_getCommand, _setCommand, None, doc="Name of command to be overridden.") absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, doc="Absolute path of the overrridden command.") ######################################################################## # CollectFile class definition ######################################################################## @total_ordering class CollectFile(object): """ Class representing a Cedar Backup collect file. The following restrictions exist on data in this class: - Absolute paths must be absolute - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The archive mode must be one of the values in :any:`VALID_ARCHIVE_MODES`. """ def __init__(self, absolutePath=None, collectMode=None, archiveMode=None): """ Constructor for the ``CollectFile`` class. Args: absolutePath: Absolute path of the file to collect collectMode: Overridden collect mode for this file archiveMode: Overridden archive mode for this file Raises: ValueError: If one of the values is invalid """ self._absolutePath = None self._collectMode = None self._archiveMode = None self.absolutePath = absolutePath self.collectMode = collectMode self.archiveMode = archiveMode def __repr__(self): """ Official string representation for class instance. """ return "CollectFile(%s, %s, %s)" % (self.absolutePath, self.collectMode, self.archiveMode) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.absolutePath != other.absolutePath: if str(self.absolutePath or "") < str(other.absolutePath or ""): return -1 else: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.archiveMode != other.archiveMode: if str(self.archiveMode or "") < str(other.archiveMode or ""): return -1 else: return 1 return 0 def _setAbsolutePath(self, value): """ Property target used to set the absolute path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Not an absolute path: [%s]" % value) self._absolutePath = encodePath(value) def _getAbsolutePath(self): """ Property target used to get the absolute path. """ return self._absolutePath def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of the values in :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setArchiveMode(self, value): """ Property target used to set the archive mode. If not ``None``, the mode must be one of the values in :any:`VALID_ARCHIVE_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_ARCHIVE_MODES: raise ValueError("Archive mode must be one of %s." % VALID_ARCHIVE_MODES) self._archiveMode = value def _getArchiveMode(self): """ Property target used to get the archive mode. """ return self._archiveMode absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, doc="Absolute path of the file to collect.") collectMode = property(_getCollectMode, _setCollectMode, None, doc="Overridden collect mode for this file.") archiveMode = property(_getArchiveMode, _setArchiveMode, None, doc="Overridden archive mode for this file.") ######################################################################## # CollectDir class definition ######################################################################## @total_ordering class CollectDir(object): """ Class representing a Cedar Backup collect directory. The following restrictions exist on data in this class: - Absolute paths must be absolute - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The archive mode must be one of the values in :any:`VALID_ARCHIVE_MODES`. - The ignore file must be a non-empty string. For the ``absoluteExcludePaths`` list, validation is accomplished through the :any:`util.AbsolutePathList` list implementation that overrides common list methods and transparently does the absolute path validation for us. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__( self, absolutePath=None, collectMode=None, archiveMode=None, ignoreFile=None, absoluteExcludePaths=None, relativeExcludePaths=None, excludePatterns=None, linkDepth=None, dereference=False, recursionLevel=None, ): """ Constructor for the ``CollectDir`` class. Args: absolutePath: Absolute path of the directory to collect collectMode: Overridden collect mode for this directory archiveMode: Overridden archive mode for this directory ignoreFile: Overidden ignore file name for this directory linkDepth: Maximum at which soft links should be followed dereference: Whether to dereference links that are followed absoluteExcludePaths: List of absolute paths to exclude relativeExcludePaths: List of relative paths to exclude excludePatterns: List of regular expression patterns to exclude Raises: ValueError: If one of the values is invalid """ self._absolutePath = None self._collectMode = None self._archiveMode = None self._ignoreFile = None self._linkDepth = None self._dereference = None self._recursionLevel = None self._absoluteExcludePaths = None self._relativeExcludePaths = None self._excludePatterns = None self.absolutePath = absolutePath self.collectMode = collectMode self.archiveMode = archiveMode self.ignoreFile = ignoreFile self.linkDepth = linkDepth self.dereference = dereference self.recursionLevel = recursionLevel self.absoluteExcludePaths = absoluteExcludePaths self.relativeExcludePaths = relativeExcludePaths self.excludePatterns = excludePatterns def __repr__(self): """ Official string representation for class instance. """ return "CollectDir(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % ( self.absolutePath, self.collectMode, self.archiveMode, self.ignoreFile, self.absoluteExcludePaths, self.relativeExcludePaths, self.excludePatterns, self.linkDepth, self.dereference, self.recursionLevel, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.absolutePath != other.absolutePath: if str(self.absolutePath or "") < str(other.absolutePath or ""): return -1 else: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.archiveMode != other.archiveMode: if str(self.archiveMode or "") < str(other.archiveMode or ""): return -1 else: return 1 if self.ignoreFile != other.ignoreFile: if str(self.ignoreFile or "") < str(other.ignoreFile or ""): return -1 else: return 1 if self.linkDepth != other.linkDepth: if int(self.linkDepth or 0) < int(other.linkDepth or 0): return -1 else: return 1 if self.dereference != other.dereference: if self.dereference < other.dereference: return -1 else: return 1 if self.recursionLevel != other.recursionLevel: if int(self.recursionLevel or 0) < int(other.recursionLevel or 0): return -1 else: return 1 if self.absoluteExcludePaths != other.absoluteExcludePaths: if self.absoluteExcludePaths < other.absoluteExcludePaths: return -1 else: return 1 if self.relativeExcludePaths != other.relativeExcludePaths: if self.relativeExcludePaths < other.relativeExcludePaths: return -1 else: return 1 if self.excludePatterns != other.excludePatterns: if self.excludePatterns < other.excludePatterns: return -1 else: return 1 return 0 def _setAbsolutePath(self, value): """ Property target used to set the absolute path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Not an absolute path: [%s]" % value) self._absolutePath = encodePath(value) def _getAbsolutePath(self): """ Property target used to get the absolute path. """ return self._absolutePath def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of the values in :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setArchiveMode(self, value): """ Property target used to set the archive mode. If not ``None``, the mode must be one of the values in :any:`VALID_ARCHIVE_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_ARCHIVE_MODES: raise ValueError("Archive mode must be one of %s." % VALID_ARCHIVE_MODES) self._archiveMode = value def _getArchiveMode(self): """ Property target used to get the archive mode. """ return self._archiveMode def _setIgnoreFile(self, value): """ Property target used to set the ignore file. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The ignore file must be a non-empty string.") self._ignoreFile = value def _getIgnoreFile(self): """ Property target used to get the ignore file. """ return self._ignoreFile def _setLinkDepth(self, value): """ Property target used to set the link depth. The value must be an integer >= 0. Raises: ValueError: If the value is not valid """ if value is None: self._linkDepth = None else: try: value = int(value) except TypeError: raise ValueError("Link depth value must be an integer >= 0.") if value < 0: raise ValueError("Link depth value must be an integer >= 0.") self._linkDepth = value def _getLinkDepth(self): """ Property target used to get the action linkDepth. """ return self._linkDepth def _setDereference(self, value): """ Property target used to set the dereference flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._dereference = True else: self._dereference = False def _getDereference(self): """ Property target used to get the dereference flag. """ return self._dereference def _setRecursionLevel(self, value): """ Property target used to set the recursionLevel. The value must be an integer. Raises: ValueError: If the value is not valid """ if value is None: self._recursionLevel = None else: try: value = int(value) except TypeError: raise ValueError("Recusion level value must be an integer.") self._recursionLevel = value def _getRecursionLevel(self): """ Property target used to get the action recursionLevel. """ return self._recursionLevel def _setAbsoluteExcludePaths(self, value): """ Property target used to set the absolute exclude paths list. Either the value must be ``None`` or each element must be an absolute path. Elements do not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path """ if value is None: self._absoluteExcludePaths = None else: try: saved = self._absoluteExcludePaths self._absoluteExcludePaths = AbsolutePathList() self._absoluteExcludePaths.extend(value) except Exception as e: self._absoluteExcludePaths = saved raise e def _getAbsoluteExcludePaths(self): """ Property target used to get the absolute exclude paths list. """ return self._absoluteExcludePaths def _setRelativeExcludePaths(self, value): """ Property target used to set the relative exclude paths list. Elements do not have to exist on disk at the time of assignment. """ if value is None: self._relativeExcludePaths = None else: try: saved = self._relativeExcludePaths self._relativeExcludePaths = UnorderedList() self._relativeExcludePaths.extend(value) except Exception as e: self._relativeExcludePaths = saved raise e def _getRelativeExcludePaths(self): """ Property target used to get the relative exclude paths list. """ return self._relativeExcludePaths def _setExcludePatterns(self, value): """ Property target used to set the exclude patterns list. """ if value is None: self._excludePatterns = None else: try: saved = self._excludePatterns self._excludePatterns = RegexList() self._excludePatterns.extend(value) except Exception as e: self._excludePatterns = saved raise e def _getExcludePatterns(self): """ Property target used to get the exclude patterns list. """ return self._excludePatterns absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, doc="Absolute path of the directory to collect.") collectMode = property(_getCollectMode, _setCollectMode, None, doc="Overridden collect mode for this directory.") archiveMode = property(_getArchiveMode, _setArchiveMode, None, doc="Overridden archive mode for this directory.") ignoreFile = property(_getIgnoreFile, _setIgnoreFile, None, doc="Overridden ignore file name for this directory.") linkDepth = property(_getLinkDepth, _setLinkDepth, None, doc="Maximum at which soft links should be followed.") dereference = property(_getDereference, _setDereference, None, doc="Whether to dereference links that are followed.") recursionLevel = property( _getRecursionLevel, _setRecursionLevel, None, "Recursion level to use for recursive directory collection" ) absoluteExcludePaths = property(_getAbsoluteExcludePaths, _setAbsoluteExcludePaths, None, "List of absolute paths to exclude.") relativeExcludePaths = property(_getRelativeExcludePaths, _setRelativeExcludePaths, None, "List of relative paths to exclude.") excludePatterns = property(_getExcludePatterns, _setExcludePatterns, None, "List of regular expression patterns to exclude.") ######################################################################## # PurgeDir class definition ######################################################################## @total_ordering class PurgeDir(object): """ Class representing a Cedar Backup purge directory. The following restrictions exist on data in this class: - The absolute path must be an absolute path - The retain days value must be an integer >= 0. """ def __init__(self, absolutePath=None, retainDays=None): """ Constructor for the ``PurgeDir`` class. Args: absolutePath: Absolute path of the directory to be purged retainDays: Number of days content within directory should be retained Raises: ValueError: If one of the values is invalid """ self._absolutePath = None self._retainDays = None self.absolutePath = absolutePath self.retainDays = retainDays def __repr__(self): """ Official string representation for class instance. """ return "PurgeDir(%s, %s)" % (self.absolutePath, self.retainDays) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.absolutePath != other.absolutePath: if str(self.absolutePath or "") < str(other.absolutePath or ""): return -1 else: return 1 if self.retainDays != other.retainDays: if int(self.retainDays or 0) < int(other.retainDays or 0): return -1 else: return 1 return 0 def _setAbsolutePath(self, value): """ Property target used to set the absolute path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Absolute path must, er, be an absolute path.") self._absolutePath = encodePath(value) def _getAbsolutePath(self): """ Property target used to get the absolute path. """ return self._absolutePath def _setRetainDays(self, value): """ Property target used to set the retain days value. The value must be an integer >= 0. Raises: ValueError: If the value is not valid """ if value is None: self._retainDays = None else: try: value = int(value) except TypeError: raise ValueError("Retain days value must be an integer >= 0.") if value < 0: raise ValueError("Retain days value must be an integer >= 0.") self._retainDays = value def _getRetainDays(self): """ Property target used to get the absolute path. """ return self._retainDays absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, "Absolute path of directory to purge.") retainDays = property(_getRetainDays, _setRetainDays, None, "Number of days content within directory should be retained.") ######################################################################## # LocalPeer class definition ######################################################################## @total_ordering class LocalPeer(object): """ Class representing a Cedar Backup peer. The following restrictions exist on data in this class: - The peer name must be a non-empty string. - The collect directory must be an absolute path. - The ignore failure mode must be one of the values in ``VALID_FAILURE_MODES``. """ def __init__(self, name=None, collectDir=None, ignoreFailureMode=None): """ Constructor for the ``LocalPeer`` class. Args: name: Name of the peer, typically a valid hostname collectDir: Collect directory to stage files from on peer ignoreFailureMode: Ignore failure mode for peer Raises: ValueError: If one of the values is invalid """ self._name = None self._collectDir = None self._ignoreFailureMode = None self.name = name self.collectDir = collectDir self.ignoreFailureMode = ignoreFailureMode def __repr__(self): """ Official string representation for class instance. """ return "LocalPeer(%s, %s, %s)" % (self.name, self.collectDir, self.ignoreFailureMode) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.name != other.name: if str(self.name or "") < str(other.name or ""): return -1 else: return 1 if self.collectDir != other.collectDir: if str(self.collectDir or "") < str(other.collectDir or ""): return -1 else: return 1 if self.ignoreFailureMode != other.ignoreFailureMode: if str(self.ignoreFailureMode or "") < str(other.ignoreFailureMode or ""): return -1 else: return 1 return 0 def _setName(self, value): """ Property target used to set the peer name. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The peer name must be a non-empty string.") self._name = value def _getName(self): """ Property target used to get the peer name. """ return self._name def _setCollectDir(self, value): """ Property target used to set the collect directory. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Collect directory must be an absolute path.") self._collectDir = encodePath(value) def _getCollectDir(self): """ Property target used to get the collect directory. """ return self._collectDir def _setIgnoreFailureMode(self, value): """ Property target used to set the ignoreFailure mode. If not ``None``, the mode must be one of the values in ``VALID_FAILURE_MODES``. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_FAILURE_MODES: raise ValueError("Ignore failure mode must be one of %s." % VALID_FAILURE_MODES) self._ignoreFailureMode = value def _getIgnoreFailureMode(self): """ Property target used to get the ignoreFailure mode. """ return self._ignoreFailureMode name = property(_getName, _setName, None, "Name of the peer, typically a valid hostname.") collectDir = property(_getCollectDir, _setCollectDir, None, "Collect directory to stage files from on peer.") ignoreFailureMode = property(_getIgnoreFailureMode, _setIgnoreFailureMode, None, "Ignore failure mode for peer.") ######################################################################## # RemotePeer class definition ######################################################################## @total_ordering class RemotePeer(object): """ Class representing a Cedar Backup peer. The following restrictions exist on data in this class: - The peer name must be a non-empty string. - The collect directory must be an absolute path. - The remote user must be a non-empty string. - The rcp command must be a non-empty string. - The rsh command must be a non-empty string. - The cback command must be a non-empty string. - Any managed action name must be a non-empty string matching ``ACTION_NAME_REGEX`` - The ignore failure mode must be one of the values in ``VALID_FAILURE_MODES``. """ def __init__( self, name=None, collectDir=None, remoteUser=None, rcpCommand=None, rshCommand=None, cbackCommand=None, managed=False, managedActions=None, ignoreFailureMode=None, ): """ Constructor for the ``RemotePeer`` class. Args: name: Name of the peer, must be a valid hostname collectDir: Collect directory to stage files from on peer remoteUser: Name of backup user on remote peer rcpCommand: Overridden rcp-compatible copy command for peer rshCommand: Overridden rsh-compatible remote shell command for peer cbackCommand: Overridden cback-compatible command to use on remote peer managed: Indicates whether this is a managed peer managedActions: Overridden set of actions that are managed on the peer ignoreFailureMode: Ignore failure mode for peer Raises: ValueError: If one of the values is invalid """ self._name = None self._collectDir = None self._remoteUser = None self._rcpCommand = None self._rshCommand = None self._cbackCommand = None self._managed = None self._managedActions = None self._ignoreFailureMode = None self.name = name self.collectDir = collectDir self.remoteUser = remoteUser self.rcpCommand = rcpCommand self.rshCommand = rshCommand self.cbackCommand = cbackCommand self.managed = managed self.managedActions = managedActions self.ignoreFailureMode = ignoreFailureMode def __repr__(self): """ Official string representation for class instance. """ return "RemotePeer(%s, %s, %s, %s, %s, %s, %s, %s, %s)" % ( self.name, self.collectDir, self.remoteUser, self.rcpCommand, self.rshCommand, self.cbackCommand, self.managed, self.managedActions, self.ignoreFailureMode, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.name != other.name: if str(self.name or "") < str(other.name or ""): return -1 else: return 1 if self.collectDir != other.collectDir: if str(self.collectDir or "") < str(other.collectDir or ""): return -1 else: return 1 if self.remoteUser != other.remoteUser: if str(self.remoteUser or "") < str(other.remoteUser or ""): return -1 else: return 1 if self.rcpCommand != other.rcpCommand: if str(self.rcpCommand or "") < str(other.rcpCommand or ""): return -1 else: return 1 if self.rshCommand != other.rshCommand: if str(self.rshCommand or "") < str(other.rshCommand or ""): return -1 else: return 1 if self.cbackCommand != other.cbackCommand: if str(self.cbackCommand or "") < str(other.cbackCommand or ""): return -1 else: return 1 if self.managed != other.managed: if str(self.managed or "") < str(other.managed or ""): return -1 else: return 1 if self.managedActions != other.managedActions: if self.managedActions < other.managedActions: return -1 else: return 1 if self.ignoreFailureMode != other.ignoreFailureMode: if str(self.ignoreFailureMode or "") < str(other.ignoreFailureMode or ""): return -1 else: return 1 return 0 def _setName(self, value): """ Property target used to set the peer name. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The peer name must be a non-empty string.") self._name = value def _getName(self): """ Property target used to get the peer name. """ return self._name def _setCollectDir(self, value): """ Property target used to set the collect directory. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Collect directory must be an absolute path.") self._collectDir = encodePath(value) def _getCollectDir(self): """ Property target used to get the collect directory. """ return self._collectDir def _setRemoteUser(self, value): """ Property target used to set the remote user. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The remote user must be a non-empty string.") self._remoteUser = value def _getRemoteUser(self): """ Property target used to get the remote user. """ return self._remoteUser def _setRcpCommand(self, value): """ Property target used to set the rcp command. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The rcp command must be a non-empty string.") self._rcpCommand = value def _getRcpCommand(self): """ Property target used to get the rcp command. """ return self._rcpCommand def _setRshCommand(self, value): """ Property target used to set the rsh command. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The rsh command must be a non-empty string.") self._rshCommand = value def _getRshCommand(self): """ Property target used to get the rsh command. """ return self._rshCommand def _setCbackCommand(self, value): """ Property target used to set the cback command. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The cback command must be a non-empty string.") self._cbackCommand = value def _getCbackCommand(self): """ Property target used to get the cback command. """ return self._cbackCommand def _setManaged(self, value): """ Property target used to set the managed flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._managed = True else: self._managed = False def _getManaged(self): """ Property target used to get the managed flag. """ return self._managed def _setManagedActions(self, value): """ Property target used to set the managed actions list. Elements do not have to exist on disk at the time of assignment. """ if value is None: self._managedActions = None else: try: saved = self._managedActions self._managedActions = RegexMatchList(ACTION_NAME_REGEX, emptyAllowed=False, prefix="Action name") self._managedActions.extend(value) except Exception as e: self._managedActions = saved raise e def _getManagedActions(self): """ Property target used to get the managed actions list. """ return self._managedActions def _setIgnoreFailureMode(self, value): """ Property target used to set the ignoreFailure mode. If not ``None``, the mode must be one of the values in ``VALID_FAILURE_MODES``. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_FAILURE_MODES: raise ValueError("Ignore failure mode must be one of %s." % VALID_FAILURE_MODES) self._ignoreFailureMode = value def _getIgnoreFailureMode(self): """ Property target used to get the ignoreFailure mode. """ return self._ignoreFailureMode name = property(_getName, _setName, None, "Name of the peer, must be a valid hostname.") collectDir = property(_getCollectDir, _setCollectDir, None, "Collect directory to stage files from on peer.") remoteUser = property(_getRemoteUser, _setRemoteUser, None, "Name of backup user on remote peer.") rcpCommand = property(_getRcpCommand, _setRcpCommand, None, "Overridden rcp-compatible copy command for peer.") rshCommand = property(_getRshCommand, _setRshCommand, None, "Overridden rsh-compatible remote shell command for peer.") cbackCommand = property(_getCbackCommand, _setCbackCommand, None, "Overridden cback-compatible command to use on remote peer.") managed = property(_getManaged, _setManaged, None, "Indicates whether this is a managed peer.") managedActions = property( _getManagedActions, _setManagedActions, None, "Overridden set of actions that are managed on the peer." ) ignoreFailureMode = property(_getIgnoreFailureMode, _setIgnoreFailureMode, None, "Ignore failure mode for peer.") ######################################################################## # ReferenceConfig class definition ######################################################################## @total_ordering class ReferenceConfig(object): """ Class representing a Cedar Backup reference configuration. The reference information is just used for saving off metadata about configuration and exists mostly for backwards-compatibility with Cedar Backup 1.x. """ def __init__(self, author=None, revision=None, description=None, generator=None): """ Constructor for the ``ReferenceConfig`` class. Args: author: Author of the configuration file revision: Revision of the configuration file description: Description of the configuration file generator: Tool that generated the configuration file """ self._author = None self._revision = None self._description = None self._generator = None self.author = author self.revision = revision self.description = description self.generator = generator def __repr__(self): """ Official string representation for class instance. """ return "ReferenceConfig(%s, %s, %s, %s)" % (self.author, self.revision, self.description, self.generator) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.author != other.author: if str(self.author or "") < str(other.author or ""): return -1 else: return 1 if self.revision != other.revision: if str(self.revision or "") < str(other.revision or ""): return -1 else: return 1 if self.description != other.description: if str(self.description or "") < str(other.description or ""): return -1 else: return 1 if self.generator != other.generator: if str(self.generator or "") < str(other.generator or ""): return -1 else: return 1 return 0 def _setAuthor(self, value): """ Property target used to set the author value. No validations. """ self._author = value def _getAuthor(self): """ Property target used to get the author value. """ return self._author def _setRevision(self, value): """ Property target used to set the revision value. No validations. """ self._revision = value def _getRevision(self): """ Property target used to get the revision value. """ return self._revision def _setDescription(self, value): """ Property target used to set the description value. No validations. """ self._description = value def _getDescription(self): """ Property target used to get the description value. """ return self._description def _setGenerator(self, value): """ Property target used to set the generator value. No validations. """ self._generator = value def _getGenerator(self): """ Property target used to get the generator value. """ return self._generator author = property(_getAuthor, _setAuthor, None, "Author of the configuration file.") revision = property(_getRevision, _setRevision, None, "Revision of the configuration file.") description = property(_getDescription, _setDescription, None, "Description of the configuration file.") generator = property(_getGenerator, _setGenerator, None, "Tool that generated the configuration file.") ######################################################################## # ExtensionsConfig class definition ######################################################################## @total_ordering class ExtensionsConfig(object): """ Class representing Cedar Backup extensions configuration. Extensions configuration is used to specify "extended actions" implemented by code external to Cedar Backup. For instance, a hypothetical third party might write extension code to collect database repository data. If they write a properly-formatted extension function, they can use the extension configuration to map a command-line Cedar Backup action (i.e. "database") to their function. The following restrictions exist on data in this class: - If set, the order mode must be one of the values in ``VALID_ORDER_MODES`` - The actions list must be a list of ``ExtendedAction`` objects. """ def __init__(self, actions=None, orderMode=None): """ Constructor for the ``ExtensionsConfig`` class. Args: actions: List of extended actions """ self._orderMode = None self._actions = None self.orderMode = orderMode self.actions = actions def __repr__(self): """ Official string representation for class instance. """ return "ExtensionsConfig(%s, %s)" % (self.orderMode, self.actions) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.orderMode != other.orderMode: if str(self.orderMode or "") < str(other.orderMode or ""): return -1 else: return 1 if self.actions != other.actions: if self.actions < other.actions: return -1 else: return 1 return 0 def _setOrderMode(self, value): """ Property target used to set the order mode. The value must be one of :any:`VALID_ORDER_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_ORDER_MODES: raise ValueError("Order mode must be one of %s." % VALID_ORDER_MODES) self._orderMode = value def _getOrderMode(self): """ Property target used to get the order mode. """ return self._orderMode def _setActions(self, value): """ Property target used to set the actions list. Either the value must be ``None`` or each element must be an ``ExtendedAction``. Raises: ValueError: If the value is not a ``ExtendedAction`` """ if value is None: self._actions = None else: try: saved = self._actions self._actions = ObjectTypeList(ExtendedAction, "ExtendedAction") self._actions.extend(value) except Exception as e: self._actions = saved raise e def _getActions(self): """ Property target used to get the actions list. """ return self._actions orderMode = property(_getOrderMode, _setOrderMode, None, "Order mode for extensions, to control execution ordering.") actions = property(_getActions, _setActions, None, "List of extended actions.") ######################################################################## # OptionsConfig class definition ######################################################################## @total_ordering class OptionsConfig(object): """ Class representing a Cedar Backup global options configuration. The options section is used to store global configuration options and defaults that can be applied to other sections. The following restrictions exist on data in this class: - The working directory must be an absolute path. - The starting day must be a day of the week in English, i.e. ``"monday"``, ``"tuesday"``, etc. - All of the other values must be non-empty strings if they are set to something other than ``None``. - The overrides list must be a list of ``CommandOverride`` objects. - The hooks list must be a list of ``ActionHook`` objects. - The cback command must be a non-empty string. - Any managed action name must be a non-empty string matching ``ACTION_NAME_REGEX`` """ def __init__( self, startingDay=None, workingDir=None, backupUser=None, backupGroup=None, rcpCommand=None, overrides=None, hooks=None, rshCommand=None, cbackCommand=None, managedActions=None, ): """ Constructor for the ``OptionsConfig`` class. Args: startingDay: Day that starts the week workingDir: Working (temporary) directory to use for backups backupUser: Effective user that backups should run as backupGroup: Effective group that backups should run as rcpCommand: Default rcp-compatible copy command for staging rshCommand: Default rsh-compatible command to use for remote shells cbackCommand: Default cback-compatible command to use on managed remote peers overrides: List of configured command path overrides, if any hooks: List of configured pre- and post-action hooks managedActions: Default set of actions that are managed on remote peers Raises: ValueError: If one of the values is invalid """ self._startingDay = None self._workingDir = None self._backupUser = None self._backupGroup = None self._rcpCommand = None self._rshCommand = None self._cbackCommand = None self._overrides = None self._hooks = None self._managedActions = None self.startingDay = startingDay self.workingDir = workingDir self.backupUser = backupUser self.backupGroup = backupGroup self.rcpCommand = rcpCommand self.rshCommand = rshCommand self.cbackCommand = cbackCommand self.overrides = overrides self.hooks = hooks self.managedActions = managedActions def __repr__(self): """ Official string representation for class instance. """ return "OptionsConfig(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % ( self.startingDay, self.workingDir, self.backupUser, self.backupGroup, self.rcpCommand, self.overrides, self.hooks, self.rshCommand, self.cbackCommand, self.managedActions, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.startingDay != other.startingDay: if str(self.startingDay or "") < str(other.startingDay or ""): return -1 else: return 1 if self.workingDir != other.workingDir: if str(self.workingDir or "") < str(other.workingDir or ""): return -1 else: return 1 if self.backupUser != other.backupUser: if str(self.backupUser or "") < str(other.backupUser or ""): return -1 else: return 1 if self.backupGroup != other.backupGroup: if str(self.backupGroup or "") < str(other.backupGroup or ""): return -1 else: return 1 if self.rcpCommand != other.rcpCommand: if str(self.rcpCommand or "") < str(other.rcpCommand or ""): return -1 else: return 1 if self.rshCommand != other.rshCommand: if str(self.rshCommand or "") < str(other.rshCommand or ""): return -1 else: return 1 if self.cbackCommand != other.cbackCommand: if str(self.cbackCommand or "") < str(other.cbackCommand or ""): return -1 else: return 1 if self.overrides != other.overrides: if self.overrides < other.overrides: return -1 else: return 1 if self.hooks != other.hooks: if self.hooks < other.hooks: return -1 else: return 1 if self.managedActions != other.managedActions: if self.managedActions < other.managedActions: return -1 else: return 1 return 0 def addOverride(self, command, absolutePath): """ If no override currently exists for the command, add one. Args: command: Name of command to be overridden absolutePath: Absolute path of the overrridden command """ override = CommandOverride(command, absolutePath) if self.overrides is None: self.overrides = [override] else: exists = False for obj in self.overrides: if obj.command == override.command: exists = True break if not exists: self.overrides.append(override) def replaceOverride(self, command, absolutePath): """ If override currently exists for the command, replace it; otherwise add it. Args: command: Name of command to be overridden absolutePath: Absolute path of the overrridden command """ override = CommandOverride(command, absolutePath) if self.overrides is None: self.overrides = [override] else: exists = False for obj in self.overrides: if obj.command == override.command: exists = True obj.absolutePath = override.absolutePath break if not exists: self.overrides.append(override) def _setStartingDay(self, value): """ Property target used to set the starting day. If it is not ``None``, the value must be a valid English day of the week, one of ``"monday"``, ``"tuesday"``, ``"wednesday"``, etc. Raises: ValueError: If the value is not a valid day of the week """ if value is not None: if value not in ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"]: raise ValueError('Starting day must be an English day of the week, i.e. "monday".') self._startingDay = value def _getStartingDay(self): """ Property target used to get the starting day. """ return self._startingDay def _setWorkingDir(self, value): """ Property target used to set the working directory. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Working directory must be an absolute path.") self._workingDir = encodePath(value) def _getWorkingDir(self): """ Property target used to get the working directory. """ return self._workingDir def _setBackupUser(self, value): """ Property target used to set the backup user. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("Backup user must be a non-empty string.") self._backupUser = value def _getBackupUser(self): """ Property target used to get the backup user. """ return self._backupUser def _setBackupGroup(self, value): """ Property target used to set the backup group. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("Backup group must be a non-empty string.") self._backupGroup = value def _getBackupGroup(self): """ Property target used to get the backup group. """ return self._backupGroup def _setRcpCommand(self, value): """ Property target used to set the rcp command. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The rcp command must be a non-empty string.") self._rcpCommand = value def _getRcpCommand(self): """ Property target used to get the rcp command. """ return self._rcpCommand def _setRshCommand(self, value): """ Property target used to set the rsh command. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The rsh command must be a non-empty string.") self._rshCommand = value def _getRshCommand(self): """ Property target used to get the rsh command. """ return self._rshCommand def _setCbackCommand(self, value): """ Property target used to set the cback command. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The cback command must be a non-empty string.") self._cbackCommand = value def _getCbackCommand(self): """ Property target used to get the cback command. """ return self._cbackCommand def _setOverrides(self, value): """ Property target used to set the command path overrides list. Either the value must be ``None`` or each element must be a ``CommandOverride``. Raises: ValueError: If the value is not a ``CommandOverride`` """ if value is None: self._overrides = None else: try: saved = self._overrides self._overrides = ObjectTypeList(CommandOverride, "CommandOverride") self._overrides.extend(value) except Exception as e: self._overrides = saved raise e def _getOverrides(self): """ Property target used to get the command path overrides list. """ return self._overrides def _setHooks(self, value): """ Property target used to set the pre- and post-action hooks list. Either the value must be ``None`` or each element must be an ``ActionHook``. Raises: ValueError: If the value is not a ``CommandOverride`` """ if value is None: self._hooks = None else: try: saved = self._hooks self._hooks = ObjectTypeList(ActionHook, "ActionHook") self._hooks.extend(value) except Exception as e: self._hooks = saved raise e def _getHooks(self): """ Property target used to get the command path hooks list. """ return self._hooks def _setManagedActions(self, value): """ Property target used to set the managed actions list. Elements do not have to exist on disk at the time of assignment. """ if value is None: self._managedActions = None else: try: saved = self._managedActions self._managedActions = RegexMatchList(ACTION_NAME_REGEX, emptyAllowed=False, prefix="Action name") self._managedActions.extend(value) except Exception as e: self._managedActions = saved raise e def _getManagedActions(self): """ Property target used to get the managed actions list. """ return self._managedActions startingDay = property(_getStartingDay, _setStartingDay, None, "Day that starts the week.") workingDir = property(_getWorkingDir, _setWorkingDir, None, "Working (temporary) directory to use for backups.") backupUser = property(_getBackupUser, _setBackupUser, None, "Effective user that backups should run as.") backupGroup = property(_getBackupGroup, _setBackupGroup, None, "Effective group that backups should run as.") rcpCommand = property(_getRcpCommand, _setRcpCommand, None, "Default rcp-compatible copy command for staging.") rshCommand = property(_getRshCommand, _setRshCommand, None, "Default rsh-compatible command to use for remote shells.") cbackCommand = property( _getCbackCommand, _setCbackCommand, None, "Default cback-compatible command to use on managed remote peers." ) overrides = property(_getOverrides, _setOverrides, None, "List of configured command path overrides, if any.") hooks = property(_getHooks, _setHooks, None, "List of configured pre- and post-action hooks.") managedActions = property( _getManagedActions, _setManagedActions, None, "Default set of actions that are managed on remote peers." ) ######################################################################## # PeersConfig class definition ######################################################################## @total_ordering class PeersConfig(object): """ Class representing Cedar Backup global peer configuration. This section contains a list of local and remote peers in a master's backup pool. The section is optional. If a master does not define this section, then all peers are unmanaged, and the stage configuration section must explicitly list any peer that is to be staged. If this section is configured, then peers may be managed or unmanaged, and the stage section peer configuration (if any) completely overrides this configuration. The following restrictions exist on data in this class: - The list of local peers must contain only ``LocalPeer`` objects - The list of remote peers must contain only ``RemotePeer`` objects *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, localPeers=None, remotePeers=None): """ Constructor for the ``PeersConfig`` class. Args: localPeers: List of local peers remotePeers: List of remote peers Raises: ValueError: If one of the values is invalid """ self._localPeers = None self._remotePeers = None self.localPeers = localPeers self.remotePeers = remotePeers def __repr__(self): """ Official string representation for class instance. """ return "PeersConfig(%s, %s)" % (self.localPeers, self.remotePeers) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.localPeers != other.localPeers: if self.localPeers < other.localPeers: return -1 else: return 1 if self.remotePeers != other.remotePeers: if self.remotePeers < other.remotePeers: return -1 else: return 1 return 0 def hasPeers(self): """ Indicates whether any peers are filled into this object. Returns: Boolean true if any local or remote peers are filled in, false otherwise """ return (self.localPeers is not None and len(self.localPeers) > 0) or ( self.remotePeers is not None and len(self.remotePeers) > 0 ) def _setLocalPeers(self, value): """ Property target used to set the local peers list. Either the value must be ``None`` or each element must be a ``LocalPeer``. Raises: ValueError: If the value is not an absolute path """ if value is None: self._localPeers = None else: try: saved = self._localPeers self._localPeers = ObjectTypeList(LocalPeer, "LocalPeer") self._localPeers.extend(value) except Exception as e: self._localPeers = saved raise e def _getLocalPeers(self): """ Property target used to get the local peers list. """ return self._localPeers def _setRemotePeers(self, value): """ Property target used to set the remote peers list. Either the value must be ``None`` or each element must be a ``RemotePeer``. Raises: ValueError: If the value is not a ``RemotePeer`` """ if value is None: self._remotePeers = None else: try: saved = self._remotePeers self._remotePeers = ObjectTypeList(RemotePeer, "RemotePeer") self._remotePeers.extend(value) except Exception as e: self._remotePeers = saved raise e def _getRemotePeers(self): """ Property target used to get the remote peers list. """ return self._remotePeers localPeers = property(_getLocalPeers, _setLocalPeers, None, "List of local peers.") remotePeers = property(_getRemotePeers, _setRemotePeers, None, "List of remote peers.") ######################################################################## # CollectConfig class definition ######################################################################## @total_ordering class CollectConfig(object): """ Class representing a Cedar Backup collect configuration. The following restrictions exist on data in this class: - The target directory must be an absolute path. - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The archive mode must be one of the values in :any:`VALID_ARCHIVE_MODES`. - The ignore file must be a non-empty string. - Each of the paths in ``absoluteExcludePaths`` must be an absolute path - The collect file list must be a list of ``CollectFile`` objects. - The collect directory list must be a list of ``CollectDir`` objects. For the ``absoluteExcludePaths`` list, validation is accomplished through the :any:`util.AbsolutePathList` list implementation that overrides common list methods and transparently does the absolute path validation for us. For the ``collectFiles`` and ``collectDirs`` list, validation is accomplished through the :any:`util.ObjectTypeList` list implementation that overrides common list methods and transparently ensures that each element has an appropriate type. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__( self, targetDir=None, collectMode=None, archiveMode=None, ignoreFile=None, absoluteExcludePaths=None, excludePatterns=None, collectFiles=None, collectDirs=None, ): """ Constructor for the ``CollectConfig`` class. Args: targetDir: Directory to collect files into collectMode: Default collect mode archiveMode: Default archive mode for collect files ignoreFile: Default ignore file name absoluteExcludePaths: List of absolute paths to exclude excludePatterns: List of regular expression patterns to exclude collectFiles: List of collect files collectDirs: List of collect directories Raises: ValueError: If one of the values is invalid """ self._targetDir = None self._collectMode = None self._archiveMode = None self._ignoreFile = None self._absoluteExcludePaths = None self._excludePatterns = None self._collectFiles = None self._collectDirs = None self.targetDir = targetDir self.collectMode = collectMode self.archiveMode = archiveMode self.ignoreFile = ignoreFile self.absoluteExcludePaths = absoluteExcludePaths self.excludePatterns = excludePatterns self.collectFiles = collectFiles self.collectDirs = collectDirs def __repr__(self): """ Official string representation for class instance. """ return "CollectConfig(%s, %s, %s, %s, %s, %s, %s, %s)" % ( self.targetDir, self.collectMode, self.archiveMode, self.ignoreFile, self.absoluteExcludePaths, self.excludePatterns, self.collectFiles, self.collectDirs, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.targetDir != other.targetDir: if str(self.targetDir or "") < str(other.targetDir or ""): return -1 else: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.archiveMode != other.archiveMode: if str(self.archiveMode or "") < str(other.archiveMode or ""): return -1 else: return 1 if self.ignoreFile != other.ignoreFile: if str(self.ignoreFile or "") < str(other.ignoreFile or ""): return -1 else: return 1 if self.absoluteExcludePaths != other.absoluteExcludePaths: if self.absoluteExcludePaths < other.absoluteExcludePaths: return -1 else: return 1 if self.excludePatterns != other.excludePatterns: if self.excludePatterns < other.excludePatterns: return -1 else: return 1 if self.collectFiles != other.collectFiles: if self.collectFiles < other.collectFiles: return -1 else: return 1 if self.collectDirs != other.collectDirs: if self.collectDirs < other.collectDirs: return -1 else: return 1 return 0 def _setTargetDir(self, value): """ Property target used to set the target directory. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Target directory must be an absolute path.") self._targetDir = encodePath(value) def _getTargetDir(self): """ Property target used to get the target directory. """ return self._targetDir def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setArchiveMode(self, value): """ Property target used to set the archive mode. If not ``None``, the mode must be one of :any:`VALID_ARCHIVE_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_ARCHIVE_MODES: raise ValueError("Archive mode must be one of %s." % VALID_ARCHIVE_MODES) self._archiveMode = value def _getArchiveMode(self): """ Property target used to get the archive mode. """ return self._archiveMode def _setIgnoreFile(self, value): """ Property target used to set the ignore file. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string ValueError: If the value cannot be encoded properly """ if value is not None: if len(value) < 1: raise ValueError("The ignore file must be a non-empty string.") self._ignoreFile = encodePath(value) def _getIgnoreFile(self): """ Property target used to get the ignore file. """ return self._ignoreFile def _setAbsoluteExcludePaths(self, value): """ Property target used to set the absolute exclude paths list. Either the value must be ``None`` or each element must be an absolute path. Elements do not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path """ if value is None: self._absoluteExcludePaths = None else: try: saved = self._absoluteExcludePaths self._absoluteExcludePaths = AbsolutePathList() self._absoluteExcludePaths.extend(value) except Exception as e: self._absoluteExcludePaths = saved raise e def _getAbsoluteExcludePaths(self): """ Property target used to get the absolute exclude paths list. """ return self._absoluteExcludePaths def _setExcludePatterns(self, value): """ Property target used to set the exclude patterns list. """ if value is None: self._excludePatterns = None else: try: saved = self._excludePatterns self._excludePatterns = RegexList() self._excludePatterns.extend(value) except Exception as e: self._excludePatterns = saved raise e def _getExcludePatterns(self): """ Property target used to get the exclude patterns list. """ return self._excludePatterns def _setCollectFiles(self, value): """ Property target used to set the collect files list. Either the value must be ``None`` or each element must be a ``CollectFile``. Raises: ValueError: If the value is not a ``CollectFile`` """ if value is None: self._collectFiles = None else: try: saved = self._collectFiles self._collectFiles = ObjectTypeList(CollectFile, "CollectFile") self._collectFiles.extend(value) except Exception as e: self._collectFiles = saved raise e def _getCollectFiles(self): """ Property target used to get the collect files list. """ return self._collectFiles def _setCollectDirs(self, value): """ Property target used to set the collect dirs list. Either the value must be ``None`` or each element must be a ``CollectDir``. Raises: ValueError: If the value is not a ``CollectDir`` """ if value is None: self._collectDirs = None else: try: saved = self._collectDirs self._collectDirs = ObjectTypeList(CollectDir, "CollectDir") self._collectDirs.extend(value) except Exception as e: self._collectDirs = saved raise e def _getCollectDirs(self): """ Property target used to get the collect dirs list. """ return self._collectDirs targetDir = property(_getTargetDir, _setTargetDir, None, "Directory to collect files into.") collectMode = property(_getCollectMode, _setCollectMode, None, "Default collect mode.") archiveMode = property(_getArchiveMode, _setArchiveMode, None, "Default archive mode for collect files.") ignoreFile = property(_getIgnoreFile, _setIgnoreFile, None, "Default ignore file name.") absoluteExcludePaths = property(_getAbsoluteExcludePaths, _setAbsoluteExcludePaths, None, "List of absolute paths to exclude.") excludePatterns = property(_getExcludePatterns, _setExcludePatterns, None, "List of regular expressions patterns to exclude.") collectFiles = property(_getCollectFiles, _setCollectFiles, None, "List of collect files.") collectDirs = property(_getCollectDirs, _setCollectDirs, None, "List of collect directories.") ######################################################################## # StageConfig class definition ######################################################################## @total_ordering class StageConfig(object): """ Class representing a Cedar Backup stage configuration. The following restrictions exist on data in this class: - The target directory must be an absolute path - The list of local peers must contain only ``LocalPeer`` objects - The list of remote peers must contain only ``RemotePeer`` objects *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, targetDir=None, localPeers=None, remotePeers=None): """ Constructor for the ``StageConfig`` class. Args: targetDir: Directory to stage files into, by peer name localPeers: List of local peers remotePeers: List of remote peers Raises: ValueError: If one of the values is invalid """ self._targetDir = None self._localPeers = None self._remotePeers = None self.targetDir = targetDir self.localPeers = localPeers self.remotePeers = remotePeers def __repr__(self): """ Official string representation for class instance. """ return "StageConfig(%s, %s, %s)" % (self.targetDir, self.localPeers, self.remotePeers) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.targetDir != other.targetDir: if str(self.targetDir or "") < str(other.targetDir or ""): return -1 else: return 1 if self.localPeers != other.localPeers: if self.localPeers < other.localPeers: return -1 else: return 1 if self.remotePeers != other.remotePeers: if self.remotePeers < other.remotePeers: return -1 else: return 1 return 0 def hasPeers(self): """ Indicates whether any peers are filled into this object. Returns: Boolean true if any local or remote peers are filled in, false otherwise """ return (self.localPeers is not None and len(self.localPeers) > 0) or ( self.remotePeers is not None and len(self.remotePeers) > 0 ) def _setTargetDir(self, value): """ Property target used to set the target directory. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Target directory must be an absolute path.") self._targetDir = encodePath(value) def _getTargetDir(self): """ Property target used to get the target directory. """ return self._targetDir def _setLocalPeers(self, value): """ Property target used to set the local peers list. Either the value must be ``None`` or each element must be a ``LocalPeer``. Raises: ValueError: If the value is not an absolute path """ if value is None: self._localPeers = None else: try: saved = self._localPeers self._localPeers = ObjectTypeList(LocalPeer, "LocalPeer") self._localPeers.extend(value) except Exception as e: self._localPeers = saved raise e def _getLocalPeers(self): """ Property target used to get the local peers list. """ return self._localPeers def _setRemotePeers(self, value): """ Property target used to set the remote peers list. Either the value must be ``None`` or each element must be a ``RemotePeer``. Raises: ValueError: If the value is not a ``RemotePeer`` """ if value is None: self._remotePeers = None else: try: saved = self._remotePeers self._remotePeers = ObjectTypeList(RemotePeer, "RemotePeer") self._remotePeers.extend(value) except Exception as e: self._remotePeers = saved raise e def _getRemotePeers(self): """ Property target used to get the remote peers list. """ return self._remotePeers targetDir = property(_getTargetDir, _setTargetDir, None, "Directory to stage files into, by peer name.") localPeers = property(_getLocalPeers, _setLocalPeers, None, "List of local peers.") remotePeers = property(_getRemotePeers, _setRemotePeers, None, "List of remote peers.") ######################################################################## # StoreConfig class definition ######################################################################## @total_ordering class StoreConfig(object): """ Class representing a Cedar Backup store configuration. The following restrictions exist on data in this class: - The source directory must be an absolute path. - The media type must be one of the values in :any:`VALID_MEDIA_TYPES`. - The device type must be one of the values in :any:`VALID_DEVICE_TYPES`. - The device path must be an absolute path. - The SCSI id, if provided, must be in the form specified by :any:`validateScsiId`. - The drive speed must be an integer >= 1 - The blanking behavior must be a ``BlankBehavior`` object - The refresh media delay must be an integer >= 0 - The eject delay must be an integer >= 0 Note that although the blanking factor must be a positive floating point number, it is stored as a string. This is done so that we can losslessly go back and forth between XML and object representations of configuration. """ def __init__( self, sourceDir=None, mediaType=None, deviceType=None, devicePath=None, deviceScsiId=None, driveSpeed=None, checkData=False, warnMidnite=False, noEject=False, checkMedia=False, blankBehavior=None, refreshMediaDelay=None, ejectDelay=None, ): """ Constructor for the ``StoreConfig`` class. Args: sourceDir: Directory whose contents should be written to media mediaType: Type of the media (see notes above) deviceType: Type of the device (optional, see notes above) devicePath: Filesystem device name for writer device, i.e. ``/dev/cdrw`` deviceScsiId: SCSI id for writer device, i.e. ``[:]scsibus,target,lun`` driveSpeed: Speed of the drive, i.e. ``2`` for 2x drive, etc checkData: Whether resulting image should be validated checkMedia: Whether media should be checked before being written to warnMidnite: Whether to generate warnings for crossing midnite noEject: Indicates that the writer device should not be ejected blankBehavior: Controls optimized blanking behavior refreshMediaDelay: Delay, in seconds, to add after refreshing media ejectDelay: Delay, in seconds, to add after ejecting media before closing the tray Raises: ValueError: If one of the values is invalid """ self._sourceDir = None self._mediaType = None self._deviceType = None self._devicePath = None self._deviceScsiId = None self._driveSpeed = None self._checkData = None self._checkMedia = None self._warnMidnite = None self._noEject = None self._blankBehavior = None self._refreshMediaDelay = None self._ejectDelay = None self.sourceDir = sourceDir self.mediaType = mediaType self.deviceType = deviceType self.devicePath = devicePath self.deviceScsiId = deviceScsiId self.driveSpeed = driveSpeed self.checkData = checkData self.checkMedia = checkMedia self.warnMidnite = warnMidnite self.noEject = noEject self.blankBehavior = blankBehavior self.refreshMediaDelay = refreshMediaDelay self.ejectDelay = ejectDelay def __repr__(self): """ Official string representation for class instance. """ return "StoreConfig(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % ( self.sourceDir, self.mediaType, self.deviceType, self.devicePath, self.deviceScsiId, self.driveSpeed, self.checkData, self.warnMidnite, self.noEject, self.checkMedia, self.blankBehavior, self.refreshMediaDelay, self.ejectDelay, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.sourceDir != other.sourceDir: if str(self.sourceDir or "") < str(other.sourceDir or ""): return -1 else: return 1 if self.mediaType != other.mediaType: if str(self.mediaType or "") < str(other.mediaType or ""): return -1 else: return 1 if self.deviceType != other.deviceType: if str(self.deviceType or "") < str(other.deviceType or ""): return -1 else: return 1 if self.devicePath != other.devicePath: if str(self.devicePath or "") < str(other.devicePath or ""): return -1 else: return 1 if self.deviceScsiId != other.deviceScsiId: if str(self.deviceScsiId or "") < str(other.deviceScsiId or ""): return -1 else: return 1 if self.driveSpeed != other.driveSpeed: if str(self.driveSpeed or "") < str(other.driveSpeed or ""): return -1 else: return 1 if self.checkData != other.checkData: if self.checkData < other.checkData: return -1 else: return 1 if self.checkMedia != other.checkMedia: if self.checkMedia < other.checkMedia: return -1 else: return 1 if self.warnMidnite != other.warnMidnite: if self.warnMidnite < other.warnMidnite: return -1 else: return 1 if self.noEject != other.noEject: if self.noEject < other.noEject: return -1 else: return 1 if self.blankBehavior != other.blankBehavior: if str(self.blankBehavior or "") < str(other.blankBehavior or ""): return -1 else: return 1 if self.refreshMediaDelay != other.refreshMediaDelay: if int(self.refreshMediaDelay or 0) < int(other.refreshMediaDelay or 0): return -1 else: return 1 if self.ejectDelay != other.ejectDelay: if int(self.ejectDelay or 0) < int(other.ejectDelay or 0): return -1 else: return 1 return 0 def _setSourceDir(self, value): """ Property target used to set the source directory. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Source directory must be an absolute path.") self._sourceDir = encodePath(value) def _getSourceDir(self): """ Property target used to get the source directory. """ return self._sourceDir def _setMediaType(self, value): """ Property target used to set the media type. The value must be one of :any:`VALID_MEDIA_TYPES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_MEDIA_TYPES: raise ValueError("Media type must be one of %s." % VALID_MEDIA_TYPES) self._mediaType = value def _getMediaType(self): """ Property target used to get the media type. """ return self._mediaType def _setDeviceType(self, value): """ Property target used to set the device type. The value must be one of :any:`VALID_DEVICE_TYPES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_DEVICE_TYPES: raise ValueError("Device type must be one of %s." % VALID_DEVICE_TYPES) self._deviceType = value def _getDeviceType(self): """ Property target used to get the device type. """ return self._deviceType def _setDevicePath(self, value): """ Property target used to set the device path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Device path must be an absolute path.") self._devicePath = encodePath(value) def _getDevicePath(self): """ Property target used to get the device path. """ return self._devicePath def _setDeviceScsiId(self, value): """ Property target used to set the SCSI id The SCSI id must be valid per :any:`validateScsiId`. Raises: ValueError: If the value is not valid """ if value is None: self._deviceScsiId = None else: self._deviceScsiId = validateScsiId(value) def _getDeviceScsiId(self): """ Property target used to get the SCSI id. """ return self._deviceScsiId def _setDriveSpeed(self, value): """ Property target used to set the drive speed. The drive speed must be valid per :any:`validateDriveSpeed`. Raises: ValueError: If the value is not valid """ self._driveSpeed = validateDriveSpeed(value) def _getDriveSpeed(self): """ Property target used to get the drive speed. """ return self._driveSpeed def _setCheckData(self, value): """ Property target used to set the check data flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._checkData = True else: self._checkData = False def _getCheckData(self): """ Property target used to get the check data flag. """ return self._checkData def _setCheckMedia(self, value): """ Property target used to set the check media flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._checkMedia = True else: self._checkMedia = False def _getCheckMedia(self): """ Property target used to get the check media flag. """ return self._checkMedia def _setWarnMidnite(self, value): """ Property target used to set the midnite warning flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._warnMidnite = True else: self._warnMidnite = False def _getWarnMidnite(self): """ Property target used to get the midnite warning flag. """ return self._warnMidnite def _setNoEject(self, value): """ Property target used to set the no-eject flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._noEject = True else: self._noEject = False def _getNoEject(self): """ Property target used to get the no-eject flag. """ return self._noEject def _setBlankBehavior(self, value): """ Property target used to set blanking behavior configuration. If not ``None``, the value must be a ``BlankBehavior`` object. Raises: ValueError: If the value is not a ``BlankBehavior`` """ if value is None: self._blankBehavior = None else: if not isinstance(value, BlankBehavior): raise ValueError("Value must be a ``BlankBehavior`` object.") self._blankBehavior = value def _getBlankBehavior(self): """ Property target used to get the blanking behavior configuration. """ return self._blankBehavior def _setRefreshMediaDelay(self, value): """ Property target used to set the refreshMediaDelay. The value must be an integer >= 0. Raises: ValueError: If the value is not valid """ if value is None: self._refreshMediaDelay = None else: try: value = int(value) except TypeError: raise ValueError("Action refreshMediaDelay value must be an integer >= 0.") if value < 0: raise ValueError("Action refreshMediaDelay value must be an integer >= 0.") if value == 0: value = None # normalize this out, since it's the default self._refreshMediaDelay = value def _getRefreshMediaDelay(self): """ Property target used to get the action refreshMediaDelay. """ return self._refreshMediaDelay def _setEjectDelay(self, value): """ Property target used to set the ejectDelay. The value must be an integer >= 0. Raises: ValueError: If the value is not valid """ if value is None: self._ejectDelay = None else: try: value = int(value) except TypeError: raise ValueError("Action ejectDelay value must be an integer >= 0.") if value < 0: raise ValueError("Action ejectDelay value must be an integer >= 0.") if value == 0: value = None # normalize this out, since it's the default self._ejectDelay = value def _getEjectDelay(self): """ Property target used to get the action ejectDelay. """ return self._ejectDelay sourceDir = property(_getSourceDir, _setSourceDir, None, "Directory whose contents should be written to media.") mediaType = property(_getMediaType, _setMediaType, None, "Type of the media (see notes above).") deviceType = property(_getDeviceType, _setDeviceType, None, "Type of the device (optional, see notes above).") devicePath = property(_getDevicePath, _setDevicePath, None, "Filesystem device name for writer device.") deviceScsiId = property(_getDeviceScsiId, _setDeviceScsiId, None, "SCSI id for writer device (optional, see notes above).") driveSpeed = property(_getDriveSpeed, _setDriveSpeed, None, "Speed of the drive.") checkData = property(_getCheckData, _setCheckData, None, "Whether resulting image should be validated.") checkMedia = property(_getCheckMedia, _setCheckMedia, None, "Whether media should be checked before being written to.") warnMidnite = property(_getWarnMidnite, _setWarnMidnite, None, "Whether to generate warnings for crossing midnite.") noEject = property(_getNoEject, _setNoEject, None, "Indicates that the writer device should not be ejected.") blankBehavior = property(_getBlankBehavior, _setBlankBehavior, None, "Controls optimized blanking behavior.") refreshMediaDelay = property( _getRefreshMediaDelay, _setRefreshMediaDelay, None, "Delay, in seconds, to add after refreshing media." ) ejectDelay = property( _getEjectDelay, _setEjectDelay, None, "Delay, in seconds, to add after ejecting media before closing the tray" ) ######################################################################## # PurgeConfig class definition ######################################################################## @total_ordering class PurgeConfig(object): """ Class representing a Cedar Backup purge configuration. The following restrictions exist on data in this class: - The purge directory list must be a list of ``PurgeDir`` objects. For the ``purgeDirs`` list, validation is accomplished through the :any:`util.ObjectTypeList` list implementation that overrides common list methods and transparently ensures that each element is a ``PurgeDir``. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, purgeDirs=None): """ Constructor for the ``Purge`` class. Args: purgeDirs: List of purge directories Raises: ValueError: If one of the values is invalid """ self._purgeDirs = None self.purgeDirs = purgeDirs def __repr__(self): """ Official string representation for class instance. """ return "PurgeConfig(%s)" % self.purgeDirs def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.purgeDirs != other.purgeDirs: if self.purgeDirs < other.purgeDirs: return -1 else: return 1 return 0 def _setPurgeDirs(self, value): """ Property target used to set the purge dirs list. Either the value must be ``None`` or each element must be a ``PurgeDir``. Raises: ValueError: If the value is not a ``PurgeDir`` """ if value is None: self._purgeDirs = None else: try: saved = self._purgeDirs self._purgeDirs = ObjectTypeList(PurgeDir, "PurgeDir") self._purgeDirs.extend(value) except Exception as e: self._purgeDirs = saved raise e def _getPurgeDirs(self): """ Property target used to get the purge dirs list. """ return self._purgeDirs purgeDirs = property(_getPurgeDirs, _setPurgeDirs, None, "List of directories to purge.") ######################################################################## # Config class definition ######################################################################## @total_ordering class Config(object): ###################### # Class documentation ###################### """ Class representing a Cedar Backup XML configuration document. The ``Config`` class is a Python object representation of a Cedar Backup XML configuration file. It is intended to be the only Python-language interface to Cedar Backup configuration on disk for both Cedar Backup itself and for external applications. The object representation is two-way: XML data can be used to create a ``Config`` object, and then changes to the object can be propogated back to disk. A ``Config`` object can even be used to create a configuration file from scratch programmatically. This class and the classes it is composed from often use Python's ``property`` construct to validate input and limit access to values. Some validations can only be done once a document is considered "complete" (see module notes for more details). Assignments to the various instance variables must match the expected type, i.e. ``reference`` must be a ``ReferenceConfig``. The internal check uses the built-in ``isinstance`` function, so it should be OK to use subclasses if you want to. If an instance variable is not set, its value will be ``None``. When an object is initialized without using an XML document, all of the values will be ``None``. Even when an object is initialized using XML, some of the values might be ``None`` because not every section is required. *Note:* Lists within this class are "unordered" for equality comparisons. """ ############## # Constructor ############## def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath``, then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`Config.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._reference = None self._extensions = None self._options = None self._peers = None self._collect = None self._stage = None self._store = None self._purge = None self.reference = None self.extensions = None self.options = None self.peers = None self.collect = None self.stage = None self.store = None self.purge = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() ######################### # String representations ######################### def __repr__(self): """ Official string representation for class instance. """ return "Config(%s, %s, %s, %s, %s, %s, %s, %s)" % ( self.reference, self.extensions, self.options, self.peers, self.collect, self.stage, self.store, self.purge, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() ############################# # Standard comparison method ############################# def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.reference != other.reference: if self.reference < other.reference: return -1 else: return 1 if self.extensions != other.extensions: if self.extensions < other.extensions: return -1 else: return 1 if self.options != other.options: if self.options < other.options: return -1 else: return 1 if self.peers != other.peers: if self.peers < other.peers: return -1 else: return 1 if self.collect != other.collect: if self.collect < other.collect: return -1 else: return 1 if self.stage != other.stage: if self.stage < other.stage: return -1 else: return 1 if self.store != other.store: if self.store < other.store: return -1 else: return 1 if self.purge != other.purge: if self.purge < other.purge: return -1 else: return 1 return 0 ############# # Properties ############# def _setReference(self, value): """ Property target used to set the reference configuration value. If not ``None``, the value must be a ``ReferenceConfig`` object. Raises: ValueError: If the value is not a ``ReferenceConfig`` """ if value is None: self._reference = None else: if not isinstance(value, ReferenceConfig): raise ValueError("Value must be a ``ReferenceConfig`` object.") self._reference = value def _getReference(self): """ Property target used to get the reference configuration value. """ return self._reference def _setExtensions(self, value): """ Property target used to set the extensions configuration value. If not ``None``, the value must be a ``ExtensionsConfig`` object. Raises: ValueError: If the value is not a ``ExtensionsConfig`` """ if value is None: self._extensions = None else: if not isinstance(value, ExtensionsConfig): raise ValueError("Value must be a ``ExtensionsConfig`` object.") self._extensions = value def _getExtensions(self): """ Property target used to get the extensions configuration value. """ return self._extensions def _setOptions(self, value): """ Property target used to set the options configuration value. If not ``None``, the value must be an ``OptionsConfig`` object. Raises: ValueError: If the value is not a ``OptionsConfig`` """ if value is None: self._options = None else: if not isinstance(value, OptionsConfig): raise ValueError("Value must be a ``OptionsConfig`` object.") self._options = value def _getOptions(self): """ Property target used to get the options configuration value. """ return self._options def _setPeers(self, value): """ Property target used to set the peers configuration value. If not ``None``, the value must be an ``PeersConfig`` object. Raises: ValueError: If the value is not a ``PeersConfig`` """ if value is None: self._peers = None else: if not isinstance(value, PeersConfig): raise ValueError("Value must be a ``PeersConfig`` object.") self._peers = value def _getPeers(self): """ Property target used to get the peers configuration value. """ return self._peers def _setCollect(self, value): """ Property target used to set the collect configuration value. If not ``None``, the value must be a ``CollectConfig`` object. Raises: ValueError: If the value is not a ``CollectConfig`` """ if value is None: self._collect = None else: if not isinstance(value, CollectConfig): raise ValueError("Value must be a ``CollectConfig`` object.") self._collect = value def _getCollect(self): """ Property target used to get the collect configuration value. """ return self._collect def _setStage(self, value): """ Property target used to set the stage configuration value. If not ``None``, the value must be a ``StageConfig`` object. Raises: ValueError: If the value is not a ``StageConfig`` """ if value is None: self._stage = None else: if not isinstance(value, StageConfig): raise ValueError("Value must be a ``StageConfig`` object.") self._stage = value def _getStage(self): """ Property target used to get the stage configuration value. """ return self._stage def _setStore(self, value): """ Property target used to set the store configuration value. If not ``None``, the value must be a ``StoreConfig`` object. Raises: ValueError: If the value is not a ``StoreConfig`` """ if value is None: self._store = None else: if not isinstance(value, StoreConfig): raise ValueError("Value must be a ``StoreConfig`` object.") self._store = value def _getStore(self): """ Property target used to get the store configuration value. """ return self._store def _setPurge(self, value): """ Property target used to set the purge configuration value. If not ``None``, the value must be a ``PurgeConfig`` object. Raises: ValueError: If the value is not a ``PurgeConfig`` """ if value is None: self._purge = None else: if not isinstance(value, PurgeConfig): raise ValueError("Value must be a ``PurgeConfig`` object.") self._purge = value def _getPurge(self): """ Property target used to get the purge configuration value. """ return self._purge reference = property(_getReference, _setReference, None, "Reference configuration in terms of a ``ReferenceConfig`` object.") extensions = property( _getExtensions, _setExtensions, None, "Extensions configuration in terms of a ``ExtensionsConfig`` object." ) options = property(_getOptions, _setOptions, None, "Options configuration in terms of a ``OptionsConfig`` object.") peers = property(_getPeers, _setPeers, None, "Peers configuration in terms of a ``PeersConfig`` object.") collect = property(_getCollect, _setCollect, None, "Collect configuration in terms of a ``CollectConfig`` object.") stage = property(_getStage, _setStage, None, "Stage configuration in terms of a ``StageConfig`` object.") store = property(_getStore, _setStore, None, "Store configuration in terms of a ``StoreConfig`` object.") purge = property(_getPurge, _setPurge, None, "Purge configuration in terms of a ``PurgeConfig`` object.") ################# # Public methods ################# def extractXml(self, xmlPath=None, validate=True): """ Extracts configuration into an XML document. If ``xmlPath`` is not provided, then the XML document will be returned as a string. If ``xmlPath`` is provided, then the XML document will be written to the file and ``None`` will be returned. Unless the ``validate`` parameter is ``False``, the :any:`Config.validate` method will be called (with its default arguments) against the configuration before extracting the XML. If configuration is not valid, then an XML document will not be extracted. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to write an invalid configuration file to disk. Args: xmlPath (Absolute path to a file): Path to an XML file to create on disk validate (Boolean true/false): Validate the document before extracting it Returns: XML string data or ``None`` as described above Raises: ValueError: If configuration within the object is not valid IOError: If there is an error writing to the file OSError: If there is an error writing to the file """ if validate: self.validate() xmlData = self._extractXml() if xmlPath is not None: with open(xmlPath, "w") as f: # pylint: disable=unspecified-encoding f.write(xmlData) return None else: return xmlData def validate( self, requireOneAction=True, requireReference=False, requireExtensions=False, requireOptions=True, requireCollect=False, requireStage=False, requireStore=False, requirePurge=False, requirePeers=False, ): """ Validates configuration represented by the object. This method encapsulates all of the validations that should apply to a fully "complete" document but are not already taken care of by earlier validations. It also provides some extra convenience functionality which might be useful to some people. The process of validation is laid out in the *Validation* section in the class notes (above). Args: requireOneAction: Require at least one of the collect, stage, store or purge sections requireReference: Require the reference section requireExtensions: Require the extensions section requireOptions: Require the options section requirePeers: Require the peers section requireCollect: Require the collect section requireStage: Require the stage section requireStore: Require the store section requirePurge: Require the purge section Raises: ValueError: If one of the validations fails """ if requireOneAction and (self.collect, self.stage, self.store, self.purge) == (None, None, None, None): raise ValueError("At least one of the collect, stage, store and purge sections is required.") if requireReference and self.reference is None: raise ValueError("The reference is section is required.") if requireExtensions and self.extensions is None: raise ValueError("The extensions is section is required.") if requireOptions and self.options is None: raise ValueError("The options is section is required.") if requirePeers and self.peers is None: raise ValueError("The peers is section is required.") if requireCollect and self.collect is None: raise ValueError("The collect is section is required.") if requireStage and self.stage is None: raise ValueError("The stage is section is required.") if requireStore and self.store is None: raise ValueError("The store is section is required.") if requirePurge and self.purge is None: raise ValueError("The purge is section is required.") self._validateContents() ##################################### # High-level methods for parsing XML ##################################### def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls individual static methods to parse each of the individual configuration sections. Most of the validation we do here has to do with whether the document can be parsed and whether any values which exist are valid. We don't do much validation as to whether required elements actually exist unless we have to to make sense of the document (instead, that's the job of the :any:`validate` method). Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._reference = Config._parseReference(parentNode) self._extensions = Config._parseExtensions(parentNode) self._options = Config._parseOptions(parentNode) self._peers = Config._parsePeers(parentNode) self._collect = Config._parseCollect(parentNode) self._stage = Config._parseStage(parentNode) self._store = Config._parseStore(parentNode) self._purge = Config._parsePurge(parentNode) @staticmethod def _parseReference(parentNode): """ Parses a reference configuration section. We read the following fields:: author //cb_config/reference/author revision //cb_config/reference/revision description //cb_config/reference/description generator //cb_config/reference/generator Args: parentNode: Parent node to search beneath Returns: ``ReferenceConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ reference = None sectionNode = readFirstChild(parentNode, "reference") if sectionNode is not None: reference = ReferenceConfig() reference.author = readString(sectionNode, "author") reference.revision = readString(sectionNode, "revision") reference.description = readString(sectionNode, "description") reference.generator = readString(sectionNode, "generator") return reference @staticmethod def _parseExtensions(parentNode): """ Parses an extensions configuration section. We read the following fields:: orderMode //cb_config/extensions/order_mode We also read groups of the following items, one list element per item:: name //cb_config/extensions/action/name module //cb_config/extensions/action/module function //cb_config/extensions/action/function index //cb_config/extensions/action/index dependencies //cb_config/extensions/action/depends The extended actions are parsed by :any:`_parseExtendedActions`. Args: parentNode: Parent node to search beneath Returns: ``ExtensionsConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ extensions = None sectionNode = readFirstChild(parentNode, "extensions") if sectionNode is not None: extensions = ExtensionsConfig() extensions.orderMode = readString(sectionNode, "order_mode") extensions.actions = Config._parseExtendedActions(sectionNode) return extensions @staticmethod def _parseOptions(parentNode): """ Parses a options configuration section. We read the following fields:: startingDay //cb_config/options/starting_day workingDir //cb_config/options/working_dir backupUser //cb_config/options/backup_user backupGroup //cb_config/options/backup_group rcpCommand //cb_config/options/rcp_command rshCommand //cb_config/options/rsh_command cbackCommand //cb_config/options/cback_command managedActions //cb_config/options/managed_actions The list of managed actions is a comma-separated list of action names. We also read groups of the following items, one list element per item:: overrides //cb_config/options/override hooks //cb_config/options/hook The overrides are parsed by :any:`_parseOverrides` and the hooks are parsed by :any:`_parseHooks`. Args: parentNode: Parent node to search beneath Returns: ``OptionsConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ options = None sectionNode = readFirstChild(parentNode, "options") if sectionNode is not None: options = OptionsConfig() options.startingDay = readString(sectionNode, "starting_day") options.workingDir = readString(sectionNode, "working_dir") options.backupUser = readString(sectionNode, "backup_user") options.backupGroup = readString(sectionNode, "backup_group") options.rcpCommand = readString(sectionNode, "rcp_command") options.rshCommand = readString(sectionNode, "rsh_command") options.cbackCommand = readString(sectionNode, "cback_command") options.overrides = Config._parseOverrides(sectionNode) options.hooks = Config._parseHooks(sectionNode) managedActions = readString(sectionNode, "managed_actions") options.managedActions = parseCommaSeparatedString(managedActions) return options @staticmethod def _parsePeers(parentNode): """ Parses a peers configuration section. We read groups of the following items, one list element per item:: localPeers //cb_config/stage/peer remotePeers //cb_config/stage/peer The individual peer entries are parsed by :any:`_parsePeerList`. Args: parentNode: Parent node to search beneath Returns: ``StageConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ peers = None sectionNode = readFirstChild(parentNode, "peers") if sectionNode is not None: peers = PeersConfig() (peers.localPeers, peers.remotePeers) = Config._parsePeerList(sectionNode) return peers @staticmethod def _parseCollect(parentNode): """ Parses a collect configuration section. We read the following individual fields:: targetDir //cb_config/collect/collect_dir collectMode //cb_config/collect/collect_mode archiveMode //cb_config/collect/archive_mode ignoreFile //cb_config/collect/ignore_file We also read groups of the following items, one list element per item:: absoluteExcludePaths //cb_config/collect/exclude/abs_path excludePatterns //cb_config/collect/exclude/pattern collectFiles //cb_config/collect/file collectDirs //cb_config/collect/dir The exclusions are parsed by :any:`_parseExclusions`, the collect files are parsed by :any:`_parseCollectFiles`, and the directories are parsed by :any:`_parseCollectDirs`. Args: parentNode: Parent node to search beneath Returns: ``CollectConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ collect = None sectionNode = readFirstChild(parentNode, "collect") if sectionNode is not None: collect = CollectConfig() collect.targetDir = readString(sectionNode, "collect_dir") collect.collectMode = readString(sectionNode, "collect_mode") collect.archiveMode = readString(sectionNode, "archive_mode") collect.ignoreFile = readString(sectionNode, "ignore_file") (collect.absoluteExcludePaths, unused, collect.excludePatterns) = Config._parseExclusions(sectionNode) collect.collectFiles = Config._parseCollectFiles(sectionNode) collect.collectDirs = Config._parseCollectDirs(sectionNode) return collect @staticmethod def _parseStage(parentNode): """ Parses a stage configuration section. We read the following individual fields:: targetDir //cb_config/stage/staging_dir We also read groups of the following items, one list element per item:: localPeers //cb_config/stage/peer remotePeers //cb_config/stage/peer The individual peer entries are parsed by :any:`_parsePeerList`. Args: parentNode: Parent node to search beneath Returns: ``StageConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ stage = None sectionNode = readFirstChild(parentNode, "stage") if sectionNode is not None: stage = StageConfig() stage.targetDir = readString(sectionNode, "staging_dir") (stage.localPeers, stage.remotePeers) = Config._parsePeerList(sectionNode) return stage @staticmethod def _parseStore(parentNode): """ Parses a store configuration section. We read the following fields:: sourceDir //cb_config/store/source_dir mediaType //cb_config/store/media_type deviceType //cb_config/store/device_type devicePath //cb_config/store/target_device deviceScsiId //cb_config/store/target_scsi_id driveSpeed //cb_config/store/drive_speed checkData //cb_config/store/check_data checkMedia //cb_config/store/check_media warnMidnite //cb_config/store/warn_midnite noEject //cb_config/store/no_eject Blanking behavior configuration is parsed by the ``_parseBlankBehavior`` method. Args: parentNode: Parent node to search beneath Returns: ``StoreConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ store = None sectionNode = readFirstChild(parentNode, "store") if sectionNode is not None: store = StoreConfig() store.sourceDir = readString(sectionNode, "source_dir") store.mediaType = readString(sectionNode, "media_type") store.deviceType = readString(sectionNode, "device_type") store.devicePath = readString(sectionNode, "target_device") store.deviceScsiId = readString(sectionNode, "target_scsi_id") store.driveSpeed = readInteger(sectionNode, "drive_speed") store.checkData = readBoolean(sectionNode, "check_data") store.checkMedia = readBoolean(sectionNode, "check_media") store.warnMidnite = readBoolean(sectionNode, "warn_midnite") store.noEject = readBoolean(sectionNode, "no_eject") store.blankBehavior = Config._parseBlankBehavior(sectionNode) store.refreshMediaDelay = readInteger(sectionNode, "refresh_media_delay") store.ejectDelay = readInteger(sectionNode, "eject_delay") return store @staticmethod def _parsePurge(parentNode): """ Parses a purge configuration section. We read groups of the following items, one list element per item:: purgeDirs //cb_config/purge/dir The individual directory entries are parsed by :any:`_parsePurgeDirs`. Args: parentNode: Parent node to search beneath Returns: ``PurgeConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ purge = None sectionNode = readFirstChild(parentNode, "purge") if sectionNode is not None: purge = PurgeConfig() purge.purgeDirs = Config._parsePurgeDirs(sectionNode) return purge @staticmethod def _parseExtendedActions(parentNode): """ Reads extended actions data from immediately beneath the parent. We read the following individual fields from each extended action:: name name module module function function index index dependencies depends Dependency information is parsed by the ``_parseDependencies`` method. Args: parentNode: Parent node to search beneath Returns: List of extended actions Raises: ValueError: If the data at the location can't be read """ lst = [] for entry in readChildren(parentNode, "action"): if isElement(entry): action = ExtendedAction() action.name = readString(entry, "name") action.module = readString(entry, "module") action.function = readString(entry, "function") action.index = readInteger(entry, "index") action.dependencies = Config._parseDependencies(entry) lst.append(action) if not lst: lst = None return lst @staticmethod def _parseExclusions(parentNode): """ Reads exclusions data from immediately beneath the parent. We read groups of the following items, one list element per item:: absolute exclude/abs_path relative exclude/rel_path patterns exclude/pattern If there are none of some pattern (i.e. no relative path items) then ``None`` will be returned for that item in the tuple. This method can be used to parse exclusions on both the collect configuration level and on the collect directory level within collect configuration. Args: parentNode: Parent node to search beneath Returns: Tuple of (absolute, relative, patterns) exclusions """ sectionNode = readFirstChild(parentNode, "exclude") if sectionNode is None: return (None, None, None) else: absolute = readStringList(sectionNode, "abs_path") relative = readStringList(sectionNode, "rel_path") patterns = readStringList(sectionNode, "pattern") return (absolute, relative, patterns) @staticmethod def _parseOverrides(parentNode): """ Reads a list of ``CommandOverride`` objects from immediately beneath the parent. We read the following individual fields:: command command absolutePath abs_path Args: parentNode: Parent node to search beneath Returns: List of ``CommandOverride`` objects or ``None`` if none are found Raises: ValueError: If some filled-in value is invalid """ lst = [] for entry in readChildren(parentNode, "override"): if isElement(entry): override = CommandOverride() override.command = readString(entry, "command") override.absolutePath = readString(entry, "abs_path") lst.append(override) if not lst: lst = None return lst @staticmethod def _parseHooks(parentNode): """ Reads a list of ``ActionHook`` objects from immediately beneath the parent. We read the following individual fields:: action action command command Args: parentNode: Parent node to search beneath Returns: List of ``ActionHook`` objects or ``None`` if none are found Raises: ValueError: If some filled-in value is invalid """ lst = [] for entry in readChildren(parentNode, "pre_action_hook"): if isElement(entry): hook = PreActionHook() hook.action = readString(entry, "action") hook.command = readString(entry, "command") lst.append(hook) for entry in readChildren(parentNode, "post_action_hook"): if isElement(entry): hook = PostActionHook() hook.action = readString(entry, "action") hook.command = readString(entry, "command") lst.append(hook) if not lst: lst = None return lst @staticmethod def _parseCollectFiles(parentNode): """ Reads a list of ``CollectFile`` objects from immediately beneath the parent. We read the following individual fields:: absolutePath abs_path collectMode mode *or* collect_mode archiveMode archive_mode The collect mode is a special case. Just a ``mode`` tag is accepted, but we prefer ``collect_mode`` for consistency with the rest of the config file and to avoid confusion with the archive mode. If both are provided, only ``mode`` will be used. Args: parentNode: Parent node to search beneath Returns: List of ``CollectFile`` objects or ``None`` if none are found Raises: ValueError: If some filled-in value is invalid """ lst = [] for entry in readChildren(parentNode, "file"): if isElement(entry): cfile = CollectFile() cfile.absolutePath = readString(entry, "abs_path") cfile.collectMode = readString(entry, "mode") if cfile.collectMode is None: cfile.collectMode = readString(entry, "collect_mode") cfile.archiveMode = readString(entry, "archive_mode") lst.append(cfile) if not lst: lst = None return lst @staticmethod def _parseCollectDirs(parentNode): """ Reads a list of ``CollectDir`` objects from immediately beneath the parent. We read the following individual fields:: absolutePath abs_path collectMode mode *or* collect_mode archiveMode archive_mode ignoreFile ignore_file linkDepth link_depth dereference dereference recursionLevel recursion_level The collect mode is a special case. Just a ``mode`` tag is accepted for backwards compatibility, but we prefer ``collect_mode`` for consistency with the rest of the config file and to avoid confusion with the archive mode. If both are provided, only ``mode`` will be used. We also read groups of the following items, one list element per item:: absoluteExcludePaths exclude/abs_path relativeExcludePaths exclude/rel_path excludePatterns exclude/pattern The exclusions are parsed by :any:`_parseExclusions`. Args: parentNode: Parent node to search beneath Returns: List of ``CollectDir`` objects or ``None`` if none are found Raises: ValueError: If some filled-in value is invalid """ lst = [] for entry in readChildren(parentNode, "dir"): if isElement(entry): cdir = CollectDir() cdir.absolutePath = readString(entry, "abs_path") cdir.collectMode = readString(entry, "mode") if cdir.collectMode is None: cdir.collectMode = readString(entry, "collect_mode") cdir.archiveMode = readString(entry, "archive_mode") cdir.ignoreFile = readString(entry, "ignore_file") cdir.linkDepth = readInteger(entry, "link_depth") cdir.dereference = readBoolean(entry, "dereference") cdir.recursionLevel = readInteger(entry, "recursion_level") (cdir.absoluteExcludePaths, cdir.relativeExcludePaths, cdir.excludePatterns) = Config._parseExclusions(entry) lst.append(cdir) if not lst: lst = None return lst @staticmethod def _parsePurgeDirs(parentNode): """ Reads a list of ``PurgeDir`` objects from immediately beneath the parent. We read the following individual fields:: absolutePath /abs_path retainDays /retain_days Args: parentNode: Parent node to search beneath Returns: List of ``PurgeDir`` objects or ``None`` if none are found Raises: ValueError: If the data at the location can't be read """ lst = [] for entry in readChildren(parentNode, "dir"): if isElement(entry): cdir = PurgeDir() cdir.absolutePath = readString(entry, "abs_path") cdir.retainDays = readInteger(entry, "retain_days") lst.append(cdir) if not lst: lst = None return lst @staticmethod def _parsePeerList(parentNode): """ Reads remote and local peer data from immediately beneath the parent. We read the following individual fields for both remote and local peers:: name name collectDir collect_dir We also read the following individual fields for remote peers only:: remoteUser backup_user rcpCommand rcp_command rshCommand rsh_command cbackCommand cback_command managed managed managedActions managed_actions Additionally, the value in the ``type`` field is used to determine whether this entry is a remote peer. If the type is ``"remote"``, it's a remote peer, and if the type is ``"local"``, it's a remote peer. If there are none of one type of peer (i.e. no local peers) then ``None`` will be returned for that item in the tuple. Args: parentNode: Parent node to search beneath Returns: Tuple of (local, remote) peer lists Raises: ValueError: If the data at the location can't be read """ localPeers = [] remotePeers = [] for entry in readChildren(parentNode, "peer"): if isElement(entry): peerType = readString(entry, "type") if peerType == "local": localPeer = LocalPeer() localPeer.name = readString(entry, "name") localPeer.collectDir = readString(entry, "collect_dir") localPeer.ignoreFailureMode = readString(entry, "ignore_failures") localPeers.append(localPeer) elif peerType == "remote": remotePeer = RemotePeer() remotePeer.name = readString(entry, "name") remotePeer.collectDir = readString(entry, "collect_dir") remotePeer.remoteUser = readString(entry, "backup_user") remotePeer.rcpCommand = readString(entry, "rcp_command") remotePeer.rshCommand = readString(entry, "rsh_command") remotePeer.cbackCommand = readString(entry, "cback_command") remotePeer.ignoreFailureMode = readString(entry, "ignore_failures") remotePeer.managed = readBoolean(entry, "managed") managedActions = readString(entry, "managed_actions") remotePeer.managedActions = parseCommaSeparatedString(managedActions) remotePeers.append(remotePeer) if not localPeers: localPeers = None if not remotePeers: remotePeers = None return (localPeers, remotePeers) @staticmethod def _parseDependencies(parentNode): """ Reads extended action dependency information from a parent node. We read the following individual fields:: runBefore depends/run_before runAfter depends/run_after Each of these fields is a comma-separated list of action names. The result is placed into an ``ActionDependencies`` object. If the dependencies parent node does not exist, ``None`` will be returned. Otherwise, an ``ActionDependencies`` object will always be created, even if it does not contain any actual dependencies in it. Args: parentNode: Parent node to search beneath Returns: ``ActionDependencies`` object or ``None`` Raises: ValueError: If the data at the location can't be read """ sectionNode = readFirstChild(parentNode, "depends") if sectionNode is None: return None else: runBefore = readString(sectionNode, "run_before") runAfter = readString(sectionNode, "run_after") beforeList = parseCommaSeparatedString(runBefore) afterList = parseCommaSeparatedString(runAfter) return ActionDependencies(beforeList, afterList) @staticmethod def _parseBlankBehavior(parentNode): """ Reads a single ``BlankBehavior`` object from immediately beneath the parent. We read the following individual fields:: blankMode blank_behavior/mode blankFactor blank_behavior/factor Args: parentNode: Parent node to search beneath Returns: ``BlankBehavior`` object or ``None`` if none if the section is not found Raises: ValueError: If some filled-in value is invalid """ blankBehavior = None sectionNode = readFirstChild(parentNode, "blank_behavior") if sectionNode is not None: blankBehavior = BlankBehavior() blankBehavior.blankMode = readString(sectionNode, "mode") blankBehavior.blankFactor = readString(sectionNode, "factor") return blankBehavior ######################################## # High-level methods for generating XML ######################################## def _extractXml(self): """ Internal method to extract configuration into an XML string. This method assumes that the internal :any:`validate` method has been called prior to extracting the XML, if the caller cares. No validation will be done internally. As a general rule, fields that are set to ``None`` will be extracted into the document as empty tags. The same goes for container tags that are filled based on lists - if the list is empty or ``None``, the container tag will be empty. """ (xmlDom, parentNode) = createOutputDom() Config._addReference(xmlDom, parentNode, self.reference) Config._addExtensions(xmlDom, parentNode, self.extensions) Config._addOptions(xmlDom, parentNode, self.options) Config._addPeers(xmlDom, parentNode, self.peers) Config._addCollect(xmlDom, parentNode, self.collect) Config._addStage(xmlDom, parentNode, self.stage) Config._addStore(xmlDom, parentNode, self.store) Config._addPurge(xmlDom, parentNode, self.purge) xmlData = serializeDom(xmlDom) xmlDom.unlink() return xmlData @staticmethod def _addReference(xmlDom, parentNode, referenceConfig): """ Adds a configuration section as the next child of a parent. We add the following fields to the document:: author //cb_config/reference/author revision //cb_config/reference/revision description //cb_config/reference/description generator //cb_config/reference/generator If ``referenceConfig`` is ``None``, then no container will be added. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to referenceConfig: Reference configuration section to be added to the document """ if referenceConfig is not None: sectionNode = addContainerNode(xmlDom, parentNode, "reference") addStringNode(xmlDom, sectionNode, "author", referenceConfig.author) addStringNode(xmlDom, sectionNode, "revision", referenceConfig.revision) addStringNode(xmlDom, sectionNode, "description", referenceConfig.description) addStringNode(xmlDom, sectionNode, "generator", referenceConfig.generator) @staticmethod def _addExtensions(xmlDom, parentNode, extensionsConfig): """ Adds an configuration section as the next child of a parent. We add the following fields to the document:: order_mode //cb_config/extensions/order_mode We also add groups of the following items, one list element per item:: actions //cb_config/extensions/action The extended action entries are added by :any:`_addExtendedAction`. If ``extensionsConfig`` is ``None``, then no container will be added. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to extensionsConfig: Extensions configuration section to be added to the document """ if extensionsConfig is not None: sectionNode = addContainerNode(xmlDom, parentNode, "extensions") addStringNode(xmlDom, sectionNode, "order_mode", extensionsConfig.orderMode) if extensionsConfig.actions is not None: for action in extensionsConfig.actions: Config._addExtendedAction(xmlDom, sectionNode, action) @staticmethod def _addOptions(xmlDom, parentNode, optionsConfig): """ Adds a configuration section as the next child of a parent. We add the following fields to the document:: startingDay //cb_config/options/starting_day workingDir //cb_config/options/working_dir backupUser //cb_config/options/backup_user backupGroup //cb_config/options/backup_group rcpCommand //cb_config/options/rcp_command rshCommand //cb_config/options/rsh_command cbackCommand //cb_config/options/cback_command managedActions //cb_config/options/managed_actions We also add groups of the following items, one list element per item:: overrides //cb_config/options/override hooks //cb_config/options/pre_action_hook hooks //cb_config/options/post_action_hook The individual override items are added by :any:`_addOverride`. The individual hook items are added by :any:`_addHook`. If ``optionsConfig`` is ``None``, then no container will be added. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to optionsConfig: Options configuration section to be added to the document """ if optionsConfig is not None: sectionNode = addContainerNode(xmlDom, parentNode, "options") addStringNode(xmlDom, sectionNode, "starting_day", optionsConfig.startingDay) addStringNode(xmlDom, sectionNode, "working_dir", optionsConfig.workingDir) addStringNode(xmlDom, sectionNode, "backup_user", optionsConfig.backupUser) addStringNode(xmlDom, sectionNode, "backup_group", optionsConfig.backupGroup) addStringNode(xmlDom, sectionNode, "rcp_command", optionsConfig.rcpCommand) addStringNode(xmlDom, sectionNode, "rsh_command", optionsConfig.rshCommand) addStringNode(xmlDom, sectionNode, "cback_command", optionsConfig.cbackCommand) managedActions = Config._buildCommaSeparatedString(optionsConfig.managedActions) addStringNode(xmlDom, sectionNode, "managed_actions", managedActions) if optionsConfig.overrides is not None: for override in optionsConfig.overrides: Config._addOverride(xmlDom, sectionNode, override) if optionsConfig.hooks is not None: for hook in optionsConfig.hooks: Config._addHook(xmlDom, sectionNode, hook) @staticmethod def _addPeers(xmlDom, parentNode, peersConfig): """ Adds a configuration section as the next child of a parent. We add groups of the following items, one list element per item:: localPeers //cb_config/peers/peer remotePeers //cb_config/peers/peer The individual local and remote peer entries are added by :any:`_addLocalPeer` and :any:`_addRemotePeer`, respectively. If ``peersConfig`` is ``None``, then no container will be added. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to peersConfig: Peers configuration section to be added to the document """ if peersConfig is not None: sectionNode = addContainerNode(xmlDom, parentNode, "peers") if peersConfig.localPeers is not None: for localPeer in peersConfig.localPeers: Config._addLocalPeer(xmlDom, sectionNode, localPeer) if peersConfig.remotePeers is not None: for remotePeer in peersConfig.remotePeers: Config._addRemotePeer(xmlDom, sectionNode, remotePeer) @staticmethod def _addCollect(xmlDom, parentNode, collectConfig): """ Adds a configuration section as the next child of a parent. We add the following fields to the document:: targetDir //cb_config/collect/collect_dir collectMode //cb_config/collect/collect_mode archiveMode //cb_config/collect/archive_mode ignoreFile //cb_config/collect/ignore_file We also add groups of the following items, one list element per item:: absoluteExcludePaths //cb_config/collect/exclude/abs_path excludePatterns //cb_config/collect/exclude/pattern collectFiles //cb_config/collect/file collectDirs //cb_config/collect/dir The individual collect files are added by :any:`_addCollectFile` and individual collect directories are added by :any:`_addCollectDir`. If ``collectConfig`` is ``None``, then no container will be added. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to collectConfig: Collect configuration section to be added to the document """ if collectConfig is not None: sectionNode = addContainerNode(xmlDom, parentNode, "collect") addStringNode(xmlDom, sectionNode, "collect_dir", collectConfig.targetDir) addStringNode(xmlDom, sectionNode, "collect_mode", collectConfig.collectMode) addStringNode(xmlDom, sectionNode, "archive_mode", collectConfig.archiveMode) addStringNode(xmlDom, sectionNode, "ignore_file", collectConfig.ignoreFile) if (collectConfig.absoluteExcludePaths is not None and collectConfig.absoluteExcludePaths != []) or ( collectConfig.excludePatterns is not None and collectConfig.excludePatterns != [] ): excludeNode = addContainerNode(xmlDom, sectionNode, "exclude") if collectConfig.absoluteExcludePaths is not None: for absolutePath in collectConfig.absoluteExcludePaths: addStringNode(xmlDom, excludeNode, "abs_path", absolutePath) if collectConfig.excludePatterns is not None: for pattern in collectConfig.excludePatterns: addStringNode(xmlDom, excludeNode, "pattern", pattern) if collectConfig.collectFiles is not None: for collectFile in collectConfig.collectFiles: Config._addCollectFile(xmlDom, sectionNode, collectFile) if collectConfig.collectDirs is not None: for collectDir in collectConfig.collectDirs: Config._addCollectDir(xmlDom, sectionNode, collectDir) @staticmethod def _addStage(xmlDom, parentNode, stageConfig): """ Adds a configuration section as the next child of a parent. We add the following fields to the document:: targetDir //cb_config/stage/staging_dir We also add groups of the following items, one list element per item:: localPeers //cb_config/stage/peer remotePeers //cb_config/stage/peer The individual local and remote peer entries are added by :any:`_addLocalPeer` and :any:`_addRemotePeer`, respectively. If ``stageConfig`` is ``None``, then no container will be added. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to stageConfig: Stage configuration section to be added to the document """ if stageConfig is not None: sectionNode = addContainerNode(xmlDom, parentNode, "stage") addStringNode(xmlDom, sectionNode, "staging_dir", stageConfig.targetDir) if stageConfig.localPeers is not None: for localPeer in stageConfig.localPeers: Config._addLocalPeer(xmlDom, sectionNode, localPeer) if stageConfig.remotePeers is not None: for remotePeer in stageConfig.remotePeers: Config._addRemotePeer(xmlDom, sectionNode, remotePeer) @staticmethod def _addStore(xmlDom, parentNode, storeConfig): """ Adds a configuration section as the next child of a parent. We add the following fields to the document:: sourceDir //cb_config/store/source_dir mediaType //cb_config/store/media_type deviceType //cb_config/store/device_type devicePath //cb_config/store/target_device deviceScsiId //cb_config/store/target_scsi_id driveSpeed //cb_config/store/drive_speed checkData //cb_config/store/check_data checkMedia //cb_config/store/check_media warnMidnite //cb_config/store/warn_midnite noEject //cb_config/store/no_eject refreshMediaDelay //cb_config/store/refresh_media_delay ejectDelay //cb_config/store/eject_delay Blanking behavior configuration is added by the :any:`_addBlankBehavior` method. If ``storeConfig`` is ``None``, then no container will be added. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to storeConfig: Store configuration section to be added to the document """ if storeConfig is not None: sectionNode = addContainerNode(xmlDom, parentNode, "store") addStringNode(xmlDom, sectionNode, "source_dir", storeConfig.sourceDir) addStringNode(xmlDom, sectionNode, "media_type", storeConfig.mediaType) addStringNode(xmlDom, sectionNode, "device_type", storeConfig.deviceType) addStringNode(xmlDom, sectionNode, "target_device", storeConfig.devicePath) addStringNode(xmlDom, sectionNode, "target_scsi_id", storeConfig.deviceScsiId) addIntegerNode(xmlDom, sectionNode, "drive_speed", storeConfig.driveSpeed) addBooleanNode(xmlDom, sectionNode, "check_data", storeConfig.checkData) addBooleanNode(xmlDom, sectionNode, "check_media", storeConfig.checkMedia) addBooleanNode(xmlDom, sectionNode, "warn_midnite", storeConfig.warnMidnite) addBooleanNode(xmlDom, sectionNode, "no_eject", storeConfig.noEject) addIntegerNode(xmlDom, sectionNode, "refresh_media_delay", storeConfig.refreshMediaDelay) addIntegerNode(xmlDom, sectionNode, "eject_delay", storeConfig.ejectDelay) Config._addBlankBehavior(xmlDom, sectionNode, storeConfig.blankBehavior) @staticmethod def _addPurge(xmlDom, parentNode, purgeConfig): """ Adds a configuration section as the next child of a parent. We add the following fields to the document:: purgeDirs //cb_config/purge/dir The individual directory entries are added by :any:`_addPurgeDir`. If ``purgeConfig`` is ``None``, then no container will be added. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to purgeConfig: Purge configuration section to be added to the document """ if purgeConfig is not None: sectionNode = addContainerNode(xmlDom, parentNode, "purge") if purgeConfig.purgeDirs is not None: for purgeDir in purgeConfig.purgeDirs: Config._addPurgeDir(xmlDom, sectionNode, purgeDir) @staticmethod def _addExtendedAction(xmlDom, parentNode, action): """ Adds an extended action container as the next child of a parent. We add the following fields to the document:: name action/name module action/module function action/function index action/index dependencies action/depends Dependencies are added by the :any:`_addDependencies` method. The node itself is created as the next child of the parent node. This method only adds one action node. The parent must loop for each action in the ``ExtensionsConfig`` object. If ``action`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to action: Purge directory to be added to the document """ if action is not None: sectionNode = addContainerNode(xmlDom, parentNode, "action") addStringNode(xmlDom, sectionNode, "name", action.name) addStringNode(xmlDom, sectionNode, "module", action.module) addStringNode(xmlDom, sectionNode, "function", action.function) addIntegerNode(xmlDom, sectionNode, "index", action.index) Config._addDependencies(xmlDom, sectionNode, action.dependencies) @staticmethod def _addOverride(xmlDom, parentNode, override): """ Adds a command override container as the next child of a parent. We add the following fields to the document:: command override/command absolutePath override/abs_path The node itself is created as the next child of the parent node. This method only adds one override node. The parent must loop for each override in the ``OptionsConfig`` object. If ``override`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to override: Command override to be added to the document """ if override is not None: sectionNode = addContainerNode(xmlDom, parentNode, "override") addStringNode(xmlDom, sectionNode, "command", override.command) addStringNode(xmlDom, sectionNode, "abs_path", override.absolutePath) @staticmethod def _addHook(xmlDom, parentNode, hook): """ Adds an action hook container as the next child of a parent. The behavior varies depending on the value of the ``before`` and ``after`` flags on the hook. If the ``before`` flag is set, it's a pre-action hook, and we'll add the following fields:: action pre_action_hook/action command pre_action_hook/command If the ``after`` flag is set, it's a post-action hook, and we'll add the following fields:: action post_action_hook/action command post_action_hook/command The or node itself is created as the next child of the parent node. This method only adds one hook node. The parent must loop for each hook in the ``OptionsConfig`` object. If ``hook`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to hook: Command hook to be added to the document """ if hook is not None: if hook.before: sectionNode = addContainerNode(xmlDom, parentNode, "pre_action_hook") else: sectionNode = addContainerNode(xmlDom, parentNode, "post_action_hook") addStringNode(xmlDom, sectionNode, "action", hook.action) addStringNode(xmlDom, sectionNode, "command", hook.command) @staticmethod def _addCollectFile(xmlDom, parentNode, collectFile): """ Adds a collect file container as the next child of a parent. We add the following fields to the document:: absolutePath dir/abs_path collectMode dir/collect_mode archiveMode dir/archive_mode Note that for consistency with collect directory handling we'll only emit the preferred ``collect_mode`` tag. The node itself is created as the next child of the parent node. This method only adds one collect file node. The parent must loop for each collect file in the ``CollectConfig`` object. If ``collectFile`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to collectFile: Collect file to be added to the document """ if collectFile is not None: sectionNode = addContainerNode(xmlDom, parentNode, "file") addStringNode(xmlDom, sectionNode, "abs_path", collectFile.absolutePath) addStringNode(xmlDom, sectionNode, "collect_mode", collectFile.collectMode) addStringNode(xmlDom, sectionNode, "archive_mode", collectFile.archiveMode) @staticmethod def _addCollectDir(xmlDom, parentNode, collectDir): """ Adds a collect directory container as the next child of a parent. We add the following fields to the document:: absolutePath dir/abs_path collectMode dir/collect_mode archiveMode dir/archive_mode ignoreFile dir/ignore_file linkDepth dir/link_depth dereference dir/dereference recursionLevel dir/recursion_level Note that an original XML document might have listed the collect mode using the ``mode`` tag, since we accept both ``collect_mode`` and ``mode``. However, here we'll only emit the preferred ``collect_mode`` tag. We also add groups of the following items, one list element per item:: absoluteExcludePaths dir/exclude/abs_path relativeExcludePaths dir/exclude/rel_path excludePatterns dir/exclude/pattern The node itself is created as the next child of the parent node. This method only adds one collect directory node. The parent must loop for each collect directory in the ``CollectConfig`` object. If ``collectDir`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to collectDir: Collect directory to be added to the document """ if collectDir is not None: sectionNode = addContainerNode(xmlDom, parentNode, "dir") addStringNode(xmlDom, sectionNode, "abs_path", collectDir.absolutePath) addStringNode(xmlDom, sectionNode, "collect_mode", collectDir.collectMode) addStringNode(xmlDom, sectionNode, "archive_mode", collectDir.archiveMode) addStringNode(xmlDom, sectionNode, "ignore_file", collectDir.ignoreFile) addIntegerNode(xmlDom, sectionNode, "link_depth", collectDir.linkDepth) addBooleanNode(xmlDom, sectionNode, "dereference", collectDir.dereference) addIntegerNode(xmlDom, sectionNode, "recursion_level", collectDir.recursionLevel) if ( (collectDir.absoluteExcludePaths is not None and collectDir.absoluteExcludePaths != []) or (collectDir.relativeExcludePaths is not None and collectDir.relativeExcludePaths != []) or (collectDir.excludePatterns is not None and collectDir.excludePatterns != []) ): excludeNode = addContainerNode(xmlDom, sectionNode, "exclude") if collectDir.absoluteExcludePaths is not None: for absolutePath in collectDir.absoluteExcludePaths: addStringNode(xmlDom, excludeNode, "abs_path", absolutePath) if collectDir.relativeExcludePaths is not None: for relativePath in collectDir.relativeExcludePaths: addStringNode(xmlDom, excludeNode, "rel_path", relativePath) if collectDir.excludePatterns is not None: for pattern in collectDir.excludePatterns: addStringNode(xmlDom, excludeNode, "pattern", pattern) @staticmethod def _addLocalPeer(xmlDom, parentNode, localPeer): """ Adds a local peer container as the next child of a parent. We add the following fields to the document:: name peer/name collectDir peer/collect_dir ignoreFailureMode peer/ignore_failures Additionally, ``peer/type`` is filled in with ``"local"``, since this is a local peer. The node itself is created as the next child of the parent node. This method only adds one peer node. The parent must loop for each peer in the ``StageConfig`` object. If ``localPeer`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to localPeer: Purge directory to be added to the document """ if localPeer is not None: sectionNode = addContainerNode(xmlDom, parentNode, "peer") addStringNode(xmlDom, sectionNode, "name", localPeer.name) addStringNode(xmlDom, sectionNode, "type", "local") addStringNode(xmlDom, sectionNode, "collect_dir", localPeer.collectDir) addStringNode(xmlDom, sectionNode, "ignore_failures", localPeer.ignoreFailureMode) @staticmethod def _addRemotePeer(xmlDom, parentNode, remotePeer): """ Adds a remote peer container as the next child of a parent. We add the following fields to the document:: name peer/name collectDir peer/collect_dir remoteUser peer/backup_user rcpCommand peer/rcp_command rcpCommand peer/rcp_command rshCommand peer/rsh_command cbackCommand peer/cback_command ignoreFailureMode peer/ignore_failures managed peer/managed managedActions peer/managed_actions Additionally, ``peer/type`` is filled in with ``"remote"``, since this is a remote peer. The node itself is created as the next child of the parent node. This method only adds one peer node. The parent must loop for each peer in the ``StageConfig`` object. If ``remotePeer`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to remotePeer: Purge directory to be added to the document """ if remotePeer is not None: sectionNode = addContainerNode(xmlDom, parentNode, "peer") addStringNode(xmlDom, sectionNode, "name", remotePeer.name) addStringNode(xmlDom, sectionNode, "type", "remote") addStringNode(xmlDom, sectionNode, "collect_dir", remotePeer.collectDir) addStringNode(xmlDom, sectionNode, "backup_user", remotePeer.remoteUser) addStringNode(xmlDom, sectionNode, "rcp_command", remotePeer.rcpCommand) addStringNode(xmlDom, sectionNode, "rsh_command", remotePeer.rshCommand) addStringNode(xmlDom, sectionNode, "cback_command", remotePeer.cbackCommand) addStringNode(xmlDom, sectionNode, "ignore_failures", remotePeer.ignoreFailureMode) addBooleanNode(xmlDom, sectionNode, "managed", remotePeer.managed) managedActions = Config._buildCommaSeparatedString(remotePeer.managedActions) addStringNode(xmlDom, sectionNode, "managed_actions", managedActions) @staticmethod def _addPurgeDir(xmlDom, parentNode, purgeDir): """ Adds a purge directory container as the next child of a parent. We add the following fields to the document:: absolutePath dir/abs_path retainDays dir/retain_days The node itself is created as the next child of the parent node. This method only adds one purge directory node. The parent must loop for each purge directory in the ``PurgeConfig`` object. If ``purgeDir`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to purgeDir: Purge directory to be added to the document """ if purgeDir is not None: sectionNode = addContainerNode(xmlDom, parentNode, "dir") addStringNode(xmlDom, sectionNode, "abs_path", purgeDir.absolutePath) addIntegerNode(xmlDom, sectionNode, "retain_days", purgeDir.retainDays) @staticmethod def _addDependencies(xmlDom, parentNode, dependencies): """ Adds a extended action dependencies to parent node. We add the following fields to the document:: runBefore depends/run_before runAfter depends/run_after If ``dependencies`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to dependencies: ``ActionDependencies`` object to be added to the document """ if dependencies is not None: sectionNode = addContainerNode(xmlDom, parentNode, "depends") runBefore = Config._buildCommaSeparatedString(dependencies.beforeList) runAfter = Config._buildCommaSeparatedString(dependencies.afterList) addStringNode(xmlDom, sectionNode, "run_before", runBefore) addStringNode(xmlDom, sectionNode, "run_after", runAfter) @staticmethod def _buildCommaSeparatedString(valueList): """ Creates a comma-separated string from a list of values. As a special case, if ``valueList`` is ``None``, then ``None`` will be returned. Args: valueList: List of values to be placed into a string Returns: Values from valueList as a comma-separated string """ if valueList is None: return None return ",".join(valueList) @staticmethod def _addBlankBehavior(xmlDom, parentNode, blankBehavior): """ Adds a blanking behavior container as the next child of a parent. We add the following fields to the document:: blankMode blank_behavior/mode blankFactor blank_behavior/factor The node itself is created as the next child of the parent node. If ``blankBehavior`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from :any:`createOutputDom` parentNode: Parent that the section should be appended to blankBehavior: Blanking behavior to be added to the document """ if blankBehavior is not None: sectionNode = addContainerNode(xmlDom, parentNode, "blank_behavior") addStringNode(xmlDom, sectionNode, "mode", blankBehavior.blankMode) addStringNode(xmlDom, sectionNode, "factor", blankBehavior.blankFactor) ################################################# # High-level methods used for validating content ################################################# def _validateContents(self): """ Validates configuration contents per rules discussed in module documentation. This is the second pass at validation. It ensures that any filled-in section contains valid data. Any sections which is not set to ``None`` is validated per the rules for that section, laid out in the module documentation (above). Raises: ValueError: If configuration is invalid """ self._validateReference() self._validateExtensions() self._validateOptions() self._validatePeers() self._validateCollect() self._validateStage() self._validateStore() self._validatePurge() def _validateReference(self): """ Validates reference configuration. There are currently no reference-related validations. Raises: ValueError: If reference configuration is invalid """ pass def _validateExtensions(self): """ Validates extensions configuration. The list of actions may be either ``None`` or an empty list ``[]`` if desired. Each extended action must include a name, a module, and a function. Then, if the order mode is None or "index", an index is required; and if the order mode is "dependency", dependency information is required. Raises: ValueError: If reference configuration is invalid """ if self.extensions is not None: if self.extensions.actions is not None: names = [] for action in self.extensions.actions: if action.name is None: raise ValueError("Each extended action must set a name.") names.append(action.name) if action.module is None: raise ValueError("Each extended action must set a module.") if action.function is None: raise ValueError("Each extended action must set a function.") if self.extensions.orderMode is None or self.extensions.orderMode == "index": if action.index is None: raise ValueError("Each extended action must set an index, based on order mode.") elif self.extensions.orderMode == "dependency": if action.dependencies is None: raise ValueError("Each extended action must set dependency information, based on order mode.") checkUnique("Duplicate extension names exist:", names) def _validateOptions(self): """ Validates options configuration. All fields must be filled in except the rsh command. The rcp and rsh commands are used as default values for all remote peers. Remote peers can also rely on the backup user as the default remote user name if they choose. Raises: ValueError: If reference configuration is invalid """ if self.options is not None: if self.options.startingDay is None: raise ValueError("Options section starting day must be filled in.") if self.options.workingDir is None: raise ValueError("Options section working directory must be filled in.") if self.options.backupUser is None: raise ValueError("Options section backup user must be filled in.") if self.options.backupGroup is None: raise ValueError("Options section backup group must be filled in.") if self.options.rcpCommand is None: raise ValueError("Options section remote copy command must be filled in.") def _validatePeers(self): """ Validates peers configuration per rules in :any:`_validatePeerList`. Raises: ValueError: If peers configuration is invalid """ if self.peers is not None: self._validatePeerList(self.peers.localPeers, self.peers.remotePeers) def _validateCollect(self): """ Validates collect configuration. The target directory must be filled in. The collect mode, archive mode, ignore file, and recursion level are all optional. The list of absolute paths to exclude and patterns to exclude may be either ``None`` or an empty list ``[]`` if desired. Each collect directory entry must contain an absolute path to collect, and then must either be able to take collect mode, archive mode and ignore file configuration from the parent ``CollectConfig`` object, or must set each value on its own. The list of absolute paths to exclude, relative paths to exclude and patterns to exclude may be either ``None`` or an empty list ``[]`` if desired. Any list of absolute paths to exclude or patterns to exclude will be combined with the same list in the ``CollectConfig`` object to make the complete list for a given directory. Raises: ValueError: If collect configuration is invalid """ if self.collect is not None: if self.collect.targetDir is None: raise ValueError("Collect section target directory must be filled in.") if self.collect.collectFiles is not None: for collectFile in self.collect.collectFiles: if collectFile.absolutePath is None: raise ValueError("Each collect file must set an absolute path.") if self.collect.collectMode is None and collectFile.collectMode is None: raise ValueError("Collect mode must either be set in parent collect section or individual collect file.") if self.collect.archiveMode is None and collectFile.archiveMode is None: raise ValueError("Archive mode must either be set in parent collect section or individual collect file.") if self.collect.collectDirs is not None: for collectDir in self.collect.collectDirs: if collectDir.absolutePath is None: raise ValueError("Each collect directory must set an absolute path.") if self.collect.collectMode is None and collectDir.collectMode is None: raise ValueError( "Collect mode must either be set in parent collect section or individual collect directory." ) if self.collect.archiveMode is None and collectDir.archiveMode is None: raise ValueError( "Archive mode must either be set in parent collect section or individual collect directory." ) if self.collect.ignoreFile is None and collectDir.ignoreFile is None: raise ValueError( "Ignore file must either be set in parent collect section or individual collect directory." ) if (collectDir.linkDepth is None or collectDir.linkDepth < 1) and collectDir.dereference: raise ValueError("Dereference flag is only valid when a non-zero link depth is in use.") def _validateStage(self): """ Validates stage configuration. The target directory must be filled in, and the peers are also validated. Peers are only required in this section if the peers configuration section is not filled in. However, if any peers are filled in here, they override the peers configuration and must meet the validation criteria in :any:`_validatePeerList`. Raises: ValueError: If stage configuration is invalid """ if self.stage is not None: if self.stage.targetDir is None: raise ValueError("Stage section target directory must be filled in.") if self.peers is None: # In this case, stage configuration is our only configuration and must be valid. self._validatePeerList(self.stage.localPeers, self.stage.remotePeers) else: # In this case, peers configuration is the default and stage configuration overrides. # Validation is only needed if it's stage configuration is actually filled in. if self.stage.hasPeers(): self._validatePeerList(self.stage.localPeers, self.stage.remotePeers) def _validateStore(self): """ Validates store configuration. The device type, drive speed, and blanking behavior are optional. All other values are required. Missing booleans will be set to defaults. If blanking behavior is provided, then both a blanking mode and a blanking factor are required. The image writer functionality in the ``writer`` module is supposed to be able to handle a device speed of ``None``. Any caller which needs a "real" (non-``None``) value for the device type can use ``DEFAULT_DEVICE_TYPE``, which is guaranteed to be sensible. This is also where we make sure that the media type -- which is already a valid type -- matches up properly with the device type. Raises: ValueError: If store configuration is invalid """ if self.store is not None: if self.store.sourceDir is None: raise ValueError("Store section source directory must be filled in.") if self.store.mediaType is None: raise ValueError("Store section media type must be filled in.") if self.store.devicePath is None: raise ValueError("Store section device path must be filled in.") if self.store.deviceType is None or self.store.deviceType == "cdwriter": if self.store.mediaType not in VALID_CD_MEDIA_TYPES: raise ValueError("Media type must match device type.") elif self.store.deviceType == "dvdwriter": if self.store.mediaType not in VALID_DVD_MEDIA_TYPES: raise ValueError("Media type must match device type.") if self.store.blankBehavior is not None: if self.store.blankBehavior.blankMode is None and self.store.blankBehavior.blankFactor is None: raise ValueError("If blanking behavior is provided, all values must be filled in.") def _validatePurge(self): """ Validates purge configuration. The list of purge directories may be either ``None`` or an empty list ``[]`` if desired. All purge directories must contain a path and a retain days value. Raises: ValueError: If purge configuration is invalid """ if self.purge is not None: if self.purge.purgeDirs is not None: for purgeDir in self.purge.purgeDirs: if purgeDir.absolutePath is None: raise ValueError("Each purge directory must set an absolute path.") if purgeDir.retainDays is None: raise ValueError("Each purge directory must set a retain days value.") def _validatePeerList(self, localPeers, remotePeers): """ Validates the set of local and remote peers. Local peers must be completely filled in, including both name and collect directory. Remote peers must also fill in the name and collect directory, but can leave the remote user and rcp command unset. In this case, the remote user is assumed to match the backup user from the options section and rcp command is taken directly from the options section. Args: localPeers: List of local peers remotePeers: List of remote peers Raises: ValueError: If stage configuration is invalid """ if localPeers is None and remotePeers is None: raise ValueError("Peer list must contain at least one backup peer.") if localPeers is None and remotePeers is not None: if len(remotePeers) < 1: raise ValueError("Peer list must contain at least one backup peer.") elif localPeers is not None and remotePeers is None: if len(localPeers) < 1: raise ValueError("Peer list must contain at least one backup peer.") elif localPeers is not None and remotePeers is not None: if len(localPeers) + len(remotePeers) < 1: raise ValueError("Peer list must contain at least one backup peer.") names = [] if localPeers is not None: for localPeer in localPeers: if localPeer.name is None: raise ValueError("Local peers must set a name.") names.append(localPeer.name) if localPeer.collectDir is None: raise ValueError("Local peers must set a collect directory.") if remotePeers is not None: for remotePeer in remotePeers: if remotePeer.name is None: raise ValueError("Remote peers must set a name.") names.append(remotePeer.name) if remotePeer.collectDir is None: raise ValueError("Remote peers must set a collect directory.") if (self.options is None or self.options.backupUser is None) and remotePeer.remoteUser is None: raise ValueError("Remote user must either be set in options section or individual remote peer.") if (self.options is None or self.options.rcpCommand is None) and remotePeer.rcpCommand is None: raise ValueError("Remote copy command must either be set in options section or individual remote peer.") if remotePeer.managed: if (self.options is None or self.options.rshCommand is None) and remotePeer.rshCommand is None: raise ValueError("Remote shell command must either be set in options section or individual remote peer.") if (self.options is None or self.options.cbackCommand is None) and remotePeer.cbackCommand is None: raise ValueError("Remote cback command must either be set in options section or individual remote peer.") if (self.options is None or self.options.managedActions is None or len(self.options.managedActions) < 1) and ( remotePeer.managedActions is None or len(remotePeer.managedActions) < 1 ): raise ValueError("Managed actions list must be set in options section or individual remote peer.") checkUnique("Duplicate peer names exist:", names) ######################################################################## # General utility functions ######################################################################## def readByteQuantity(parent, name): """ Read a byte size value from an XML document. A byte size value is an interpreted string value. If the string value ends with "MB" or "GB", then the string before that is interpreted as megabytes or gigabytes. Otherwise, it is intepreted as bytes. Args: parent: Parent node to search beneath name: Name of node to search for Returns: ByteQuantity parsed from XML document """ data = readString(parent, name) if data is None: return None data = data.strip() if data.endswith("KB"): quantity = data[0 : data.rfind("KB")].strip() units = UNIT_KBYTES elif data.endswith("MB"): quantity = data[0 : data.rfind("MB")].strip() units = UNIT_MBYTES elif data.endswith("GB"): quantity = data[0 : data.rfind("GB")].strip() units = UNIT_GBYTES else: quantity = data.strip() units = UNIT_BYTES return ByteQuantity(quantity, units) def addByteQuantityNode(xmlDom, parentNode, nodeName, byteQuantity): """ Adds a text node as the next child of a parent, to contain a byte size. If the ``byteQuantity`` is None, then the node will be created, but will be empty (i.e. will contain no text node child). The size in bytes will be normalized. If it is larger than 1.0 GB, it will be shown in GB ("1.0 GB"). If it is larger than 1.0 MB ("1.0 MB"), it will be shown in MB. Otherwise, it will be shown in bytes ("423413"). Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent node to create child for nodeName: Name of the new container node byteQuantity: ByteQuantity object to put into the XML document Returns: Reference to the newly-created node """ if byteQuantity is None: byteString = None elif byteQuantity.units == UNIT_KBYTES: byteString = "%s KB" % byteQuantity.quantity elif byteQuantity.units == UNIT_MBYTES: byteString = "%s MB" % byteQuantity.quantity elif byteQuantity.units == UNIT_GBYTES: byteString = "%s GB" % byteQuantity.quantity else: byteString = byteQuantity.quantity return addStringNode(xmlDom, parentNode, nodeName, byteString) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/customize.py0000644000000000000000000000660314567004737016573 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Implements customized behavior. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Implements customized behavior. Some behaviors need to vary when packaged for certain platforms. For instance, while Cedar Backup generally uses cdrecord and mkisofs, Debian ships compatible utilities called wodim and genisoimage. I want there to be one single place where Cedar Backup is patched for Debian, rather than having to maintain a variety of patches in different places. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.customize") PLATFORM = "standard" # PLATFORM = "debian" DEBIAN_CDRECORD = "/usr/bin/wodim" DEBIAN_MKISOFS = "/usr/bin/genisoimage" ####################################################################### # Public functions ####################################################################### ################################ # customizeOverrides() function ################################ def customizeOverrides(config, platform=PLATFORM): """ Modify command overrides based on the configured platform. On some platforms, we want to add command overrides to configuration. Each override will only be added if the configuration does not already contain an override with the same name. That way, the user still has a way to choose their own version of the command if they want. Args: config: Configuration to modify platform: Platform that is in use """ if platform == "debian": logger.info("Overriding cdrecord for Debian platform: %s", DEBIAN_CDRECORD) config.options.addOverride("cdrecord", DEBIAN_CDRECORD) logger.info("Overriding mkisofs for Debian platform: %s", DEBIAN_MKISOFS) config.options.addOverride("mkisofs", DEBIAN_MKISOFS) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/extend/__init__.py0000644000000000000000000000323714567004737017577 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides package initialization # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Official Cedar Backup Extensions This package provides official Cedar Backup extensions. These are Cedar Backup actions that are not part of the "standard" set of Cedar Backup actions, but are officially supported along with Cedar Backup. :author: Kenneth J. Pronovici """ ######################################################################## # Package initialization ######################################################################## # Using 'from CedarBackup3.extend import *' will just import the modules listed # in the __all__ variable. import CedarBackup3.extend.amazons3 import CedarBackup3.extend.encrypt import CedarBackup3.extend.mbox import CedarBackup3.extend.mysql import CedarBackup3.extend.postgresql import CedarBackup3.extend.split import CedarBackup3.extend.subversion import CedarBackup3.extend.sysinfo __all__ = ["amazons3", "encrypt", "mbox", "mysql", "postgresql", "split", "subversion", "sysinfo"] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/extend/amazons3.py0000644000000000000000000010710014567004737017565 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2014-2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : "Store" type extension that writes data to Amazon S3. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Store-type extension that writes data to Amazon S3. This extension requires a new configuration section and is intended to be run immediately after the standard stage action, replacing the standard store action. Aside from its own configuration, it requires the options and staging configuration sections in the standard Cedar Backup configuration file. Since it is intended to replace the store action, it does not rely on any store configuration. The underlying functionality relies on the U{AWS CLI interface }. Before you use this extension, you need to set up your Amazon S3 account and configure the AWS CLI connection per Amazon's documentation. The extension assumes that the backup is being executed as root, and switches over to the configured backup user to communicate with AWS. So, make sure you configure AWS CLI as the backup user and not root. You can optionally configure Cedar Backup to encrypt data before sending it to S3. To do that, provide a complete command line using the ``${input``} and ``${output``} variables to represent the original input file and the encrypted output file. This command will be executed as the backup user. For instance, you can use something like this with GPG:: /usr/bin/gpg -c --no-use-agent --batch --yes --passphrase-file /home/backup/.passphrase -o ${output} ${input} The GPG mechanism depends on a strong passphrase for security. One way to generate a strong passphrase is using your system random number generator, i.e.:: dd if=/dev/urandom count=20 bs=1 | xxd -ps (See U{StackExchange } for more details about that advice.) If you decide to use encryption, make sure you save off the passphrase in a safe place, so you can get at your backup data later if you need to. And obviously, make sure to set permissions on the passphrase file so it can only be read by the backup user. This extension was written for and tested on Linux. It will throw an exception if run on Windows. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import datetime import json import logging import os import shutil import sys import tempfile from functools import total_ordering from CedarBackup3.actions.constants import DIR_TIME_FORMAT, STAGE_INDICATOR from CedarBackup3.actions.util import writeIndicatorFile from CedarBackup3.config import ByteQuantity, addByteQuantityNode, readByteQuantity from CedarBackup3.filesystem import BackupFileList, FilesystemList from CedarBackup3.util import ( UNIT_BYTES, changeOwnership, displayBytes, executeCommand, isRunningAsRoot, isStartOfWeek, pathJoin, resolveCommand, ) from CedarBackup3.xmlutil import ( addBooleanNode, addContainerNode, addStringNode, createInputDom, readBoolean, readFirstChild, readString, ) ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.amazons3") SU_COMMAND = ["su"] AWS_COMMAND = ["aws"] STORE_INDICATOR = "cback.amazons3" ######################################################################## # AmazonS3Config class definition ######################################################################## @total_ordering class AmazonS3Config(object): """ Class representing Amazon S3 configuration. Amazon S3 configuration is used for storing backup data in Amazon's S3 cloud storage using the ``s3cmd`` tool. The following restrictions exist on data in this class: - The s3Bucket value must be a non-empty string - The encryptCommand value, if set, must be a non-empty string - The full backup size limit, if set, must be a ByteQuantity >= 0 - The incremental backup size limit, if set, must be a ByteQuantity >= 0 """ def __init__( self, warnMidnite=None, s3Bucket=None, encryptCommand=None, fullBackupSizeLimit=None, incrementalBackupSizeLimit=None ): """ Constructor for the ``AmazonS3Config`` class. Args: warnMidnite: Whether to generate warnings for crossing midnite s3Bucket: Name of the Amazon S3 bucket in which to store the data encryptCommand: Command used to encrypt backup data before upload to S3 fullBackupSizeLimit: Maximum size of a full backup, a ByteQuantity incrementalBackupSizeLimit: Maximum size of an incremental backup, a ByteQuantity Raises: ValueError: If one of the values is invalid """ self._warnMidnite = None self._s3Bucket = None self._encryptCommand = None self._fullBackupSizeLimit = None self._incrementalBackupSizeLimit = None self.warnMidnite = warnMidnite self.s3Bucket = s3Bucket self.encryptCommand = encryptCommand self.fullBackupSizeLimit = fullBackupSizeLimit self.incrementalBackupSizeLimit = incrementalBackupSizeLimit def __repr__(self): """ Official string representation for class instance. """ return "AmazonS3Config(%s, %s, %s, %s, %s)" % ( self.warnMidnite, self.s3Bucket, self.encryptCommand, self.fullBackupSizeLimit, self.incrementalBackupSizeLimit, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.warnMidnite != other.warnMidnite: if self.warnMidnite < other.warnMidnite: return -1 else: return 1 if self.s3Bucket != other.s3Bucket: if str(self.s3Bucket or "") < str(other.s3Bucket or ""): return -1 else: return 1 if self.encryptCommand != other.encryptCommand: if str(self.encryptCommand or "") < str(other.encryptCommand or ""): return -1 else: return 1 if self.fullBackupSizeLimit != other.fullBackupSizeLimit: if (self.fullBackupSizeLimit or ByteQuantity()) < (other.fullBackupSizeLimit or ByteQuantity()): return -1 else: return 1 if self.incrementalBackupSizeLimit != other.incrementalBackupSizeLimit: if (self.incrementalBackupSizeLimit or ByteQuantity()) < (other.incrementalBackupSizeLimit or ByteQuantity()): return -1 else: return 1 return 0 def _setWarnMidnite(self, value): """ Property target used to set the midnite warning flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._warnMidnite = True else: self._warnMidnite = False def _getWarnMidnite(self): """ Property target used to get the midnite warning flag. """ return self._warnMidnite def _setS3Bucket(self, value): """ Property target used to set the S3 bucket. """ if value is not None: if len(value) < 1: raise ValueError("S3 bucket must be non-empty string.") self._s3Bucket = value def _getS3Bucket(self): """ Property target used to get the S3 bucket. """ return self._s3Bucket def _setEncryptCommand(self, value): """ Property target used to set the encrypt command. """ if value is not None: if len(value) < 1: raise ValueError("Encrypt command must be non-empty string.") self._encryptCommand = value def _getEncryptCommand(self): """ Property target used to get the encrypt command. """ return self._encryptCommand def _setFullBackupSizeLimit(self, value): """ Property target used to set the full backup size limit. The value must be an integer >= 0. Raises: ValueError: If the value is not valid """ if value is None: self._fullBackupSizeLimit = None else: if isinstance(value, ByteQuantity): self._fullBackupSizeLimit = value else: self._fullBackupSizeLimit = ByteQuantity(value, UNIT_BYTES) def _getFullBackupSizeLimit(self): """ Property target used to get the full backup size limit. """ return self._fullBackupSizeLimit def _setIncrementalBackupSizeLimit(self, value): """ Property target used to set the incremental backup size limit. The value must be an integer >= 0. Raises: ValueError: If the value is not valid """ if value is None: self._incrementalBackupSizeLimit = None else: if isinstance(value, ByteQuantity): self._incrementalBackupSizeLimit = value else: self._incrementalBackupSizeLimit = ByteQuantity(value, UNIT_BYTES) def _getIncrementalBackupSizeLimit(self): """ Property target used to get the incremental backup size limit. """ return self._incrementalBackupSizeLimit warnMidnite = property(_getWarnMidnite, _setWarnMidnite, None, "Whether to generate warnings for crossing midnite.") s3Bucket = property(_getS3Bucket, _setS3Bucket, None, doc="Amazon S3 Bucket in which to store data") encryptCommand = property(_getEncryptCommand, _setEncryptCommand, None, doc="Command used to encrypt data before upload to S3") fullBackupSizeLimit = property( _getFullBackupSizeLimit, _setFullBackupSizeLimit, None, doc="Maximum size of a full backup, as a ByteQuantity" ) incrementalBackupSizeLimit = property( _getIncrementalBackupSizeLimit, _setIncrementalBackupSizeLimit, None, doc="Maximum size of an incremental backup, as a ByteQuantity", ) ######################################################################## # LocalConfig class definition ######################################################################## @total_ordering class LocalConfig(object): """ Class representing this extension's configuration document. This is not a general-purpose configuration object like the main Cedar Backup configuration object. Instead, it just knows how to parse and emit amazons3-specific configuration values. Third parties who need to read and write configuration related to this extension should access it through the constructor, ``validate`` and ``addConfig`` methods. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath`` then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._amazons3 = None self.amazons3 = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() def __repr__(self): """ Official string representation for class instance. """ return "LocalConfig(%s)" % (self.amazons3) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.amazons3 != other.amazons3: if self.amazons3 < other.amazons3: return -1 else: return 1 return 0 def _setAmazonS3(self, value): """ Property target used to set the amazons3 configuration value. If not ``None``, the value must be a ``AmazonS3Config`` object. Raises: ValueError: If the value is not a ``AmazonS3Config`` """ if value is None: self._amazons3 = None else: if not isinstance(value, AmazonS3Config): raise ValueError("Value must be a ``AmazonS3Config`` object.") self._amazons3 = value def _getAmazonS3(self): """ Property target used to get the amazons3 configuration value. """ return self._amazons3 amazons3 = property(_getAmazonS3, _setAmazonS3, None, "AmazonS3 configuration in terms of a ``AmazonS3Config`` object.") def validate(self): """ Validates configuration represented by the object. AmazonS3 configuration must be filled in. Within that, the s3Bucket target must be filled in Raises: ValueError: If one of the validations fails """ if self.amazons3 is None: raise ValueError("AmazonS3 section is required.") if self.amazons3.s3Bucket is None: raise ValueError("AmazonS3 s3Bucket must be set.") def addConfig(self, xmlDom, parentNode): """ Adds an configuration section as the next child of a parent. Third parties should use this function to write configuration related to this extension. We add the following fields to the document:: warnMidnite //cb_config/amazons3/warn_midnite s3Bucket //cb_config/amazons3/s3_bucket encryptCommand //cb_config/amazons3/encrypt fullBackupSizeLimit //cb_config/amazons3/full_size_limit incrementalBackupSizeLimit //cb_config/amazons3/incr_size_limit Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to """ if self.amazons3 is not None: sectionNode = addContainerNode(xmlDom, parentNode, "amazons3") addBooleanNode(xmlDom, sectionNode, "warn_midnite", self.amazons3.warnMidnite) addStringNode(xmlDom, sectionNode, "s3_bucket", self.amazons3.s3Bucket) addStringNode(xmlDom, sectionNode, "encrypt", self.amazons3.encryptCommand) addByteQuantityNode(xmlDom, sectionNode, "full_size_limit", self.amazons3.fullBackupSizeLimit) addByteQuantityNode(xmlDom, sectionNode, "incr_size_limit", self.amazons3.incrementalBackupSizeLimit) def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls a static method to parse the amazons3 configuration section. Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._amazons3 = LocalConfig._parseAmazonS3(parentNode) @staticmethod def _parseAmazonS3(parent): """ Parses an amazons3 configuration section. We read the following individual fields:: warnMidnite //cb_config/amazons3/warn_midnite s3Bucket //cb_config/amazons3/s3_bucket encryptCommand //cb_config/amazons3/encrypt fullBackupSizeLimit //cb_config/amazons3/full_size_limit incrementalBackupSizeLimit //cb_config/amazons3/incr_size_limit Args: parent: Parent node to search beneath Returns: ``AmazonS3Config`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ amazons3 = None section = readFirstChild(parent, "amazons3") if section is not None: amazons3 = AmazonS3Config() amazons3.warnMidnite = readBoolean(section, "warn_midnite") amazons3.s3Bucket = readString(section, "s3_bucket") amazons3.encryptCommand = readString(section, "encrypt") amazons3.fullBackupSizeLimit = readByteQuantity(section, "full_size_limit") amazons3.incrementalBackupSizeLimit = readByteQuantity(section, "incr_size_limit") return amazons3 ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### def executeAction(configPath, options, config): """ Executes the amazons3 backup action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If there are I/O problems reading or writing files """ logger.debug("Executing amazons3 extended action.") if not isRunningAsRoot(): logger.error("Error: the amazons3 extended action must be run as root.") raise ValueError("The amazons3 extended action must be run as root.") if sys.platform == "win32": logger.error("Error: the amazons3 extended action is not supported on Windows.") raise ValueError("The amazons3 extended action is not supported on Windows.") if config.options is None or config.stage is None: raise ValueError("Cedar Backup configuration is not properly filled in.") local = LocalConfig(xmlPath=configPath) stagingDirs = _findCorrectDailyDir(options, config, local) _applySizeLimits(options, config, local, stagingDirs) _writeToAmazonS3(config, local, stagingDirs) _writeStoreIndicator(config, stagingDirs) logger.info("Executed the amazons3 extended action successfully.") ######################################################################## # Private utility functions ######################################################################## ######################### # _findCorrectDailyDir() ######################### def _findCorrectDailyDir(options, config, local): """ Finds the correct daily staging directory to be written to Amazon S3. This is substantially similar to the same function in store.py. The main difference is that it doesn't rely on store configuration at all. Args: options: Options object config: Config object local: Local config object Returns: Correct staging dir, as a dict mapping directory to date suffix Raises: IOError: If the staging directory cannot be found """ oneDay = datetime.timedelta(days=1) today = datetime.date.today() yesterday = today - oneDay tomorrow = today + oneDay todayDate = today.strftime(DIR_TIME_FORMAT) yesterdayDate = yesterday.strftime(DIR_TIME_FORMAT) tomorrowDate = tomorrow.strftime(DIR_TIME_FORMAT) todayPath = pathJoin(config.stage.targetDir, todayDate) yesterdayPath = pathJoin(config.stage.targetDir, yesterdayDate) tomorrowPath = pathJoin(config.stage.targetDir, tomorrowDate) todayStageInd = pathJoin(todayPath, STAGE_INDICATOR) yesterdayStageInd = pathJoin(yesterdayPath, STAGE_INDICATOR) tomorrowStageInd = pathJoin(tomorrowPath, STAGE_INDICATOR) todayStoreInd = pathJoin(todayPath, STORE_INDICATOR) yesterdayStoreInd = pathJoin(yesterdayPath, STORE_INDICATOR) tomorrowStoreInd = pathJoin(tomorrowPath, STORE_INDICATOR) if options.full: if os.path.isdir(todayPath) and os.path.exists(todayStageInd): logger.info("Amazon S3 process will use current day's staging directory [%s]", todayPath) return {todayPath: todayDate} raise IOError("Unable to find staging directory to process (only tried today due to full option).") else: if os.path.isdir(todayPath) and os.path.exists(todayStageInd) and not os.path.exists(todayStoreInd): logger.info("Amazon S3 process will use current day's staging directory [%s]", todayPath) return {todayPath: todayDate} elif os.path.isdir(yesterdayPath) and os.path.exists(yesterdayStageInd) and not os.path.exists(yesterdayStoreInd): logger.info("Amazon S3 process will use previous day's staging directory [%s]", yesterdayPath) if local.amazons3.warnMidnite: logger.warning("Warning: Amazon S3 process crossed midnite boundary to find data.") return {yesterdayPath: yesterdayDate} elif os.path.isdir(tomorrowPath) and os.path.exists(tomorrowStageInd) and not os.path.exists(tomorrowStoreInd): logger.info("Amazon S3 process will use next day's staging directory [%s]", tomorrowPath) if local.amazons3.warnMidnite: logger.warning("Warning: Amazon S3 process crossed midnite boundary to find data.") return {tomorrowPath: tomorrowDate} raise IOError("Unable to find unused staging directory to process (tried today, yesterday, tomorrow).") ############################## # _applySizeLimits() function ############################## def _applySizeLimits(options, config, local, stagingDirs): """ Apply size limits, throwing an exception if any limits are exceeded. Size limits are optional. If a limit is set to None, it does not apply. The full size limit applies if the full option is set or if today is the start of the week. The incremental size limit applies otherwise. Limits are applied to the total size of all the relevant staging directories. Args: options: Options object config: Config object local: Local config object stagingDirs: Dictionary mapping directory path to date suffix Raises: ValueError: Under many generic error conditions ValueError: If a size limit has been exceeded """ if options.full or isStartOfWeek(config.options.startingDay): logger.debug("Using Amazon S3 size limit for full backups.") limit = local.amazons3.fullBackupSizeLimit else: logger.debug("Using Amazon S3 size limit for incremental backups.") limit = local.amazons3.incrementalBackupSizeLimit if limit is None: logger.debug("No Amazon S3 size limit will be applied.") else: logger.debug("Amazon S3 size limit is: %s", limit) contents = BackupFileList() for stagingDir in stagingDirs: contents.addDirContents(stagingDir) total = contents.totalSize() logger.debug("Amazon S3 backup size is: %s", displayBytes(total)) if total > limit: logger.error("Amazon S3 size limit exceeded: %s > %s", displayBytes(total), limit) raise ValueError("Amazon S3 size limit exceeded: %s > %s" % (displayBytes(total), limit)) else: logger.info("Total size does not exceed Amazon S3 size limit, so backup can continue.") ############################## # _writeToAmazonS3() function ############################## def _writeToAmazonS3(config, local, stagingDirs): """ Writes the indicated staging directories to an Amazon S3 bucket. Each of the staging directories listed in ``stagingDirs`` will be written to the configured Amazon S3 bucket from local configuration. The directories will be placed into the image at the root by date, so staging directory ``/opt/stage/2005/02/10`` will be placed into the S3 bucket at ``/2005/02/10``. If an encrypt commmand is provided, the files will be encrypted first. Args: config: Config object local: Local config object stagingDirs: Dictionary mapping directory path to date suffix Raises: ValueError: Under many generic error conditions IOError: If there is a problem writing to Amazon S3 """ for stagingDir in list(stagingDirs.keys()): logger.debug("Storing stage directory to Amazon S3 [%s].", stagingDir) dateSuffix = stagingDirs[stagingDir] s3BucketUrl = "s3://%s/%s" % (local.amazons3.s3Bucket, dateSuffix) logger.debug("S3 bucket URL is [%s]", s3BucketUrl) _clearExistingBackup(config, s3BucketUrl) if local.amazons3.encryptCommand is None: logger.debug("Encryption is disabled; files will be uploaded in cleartext.") _uploadStagingDir(config, stagingDir, s3BucketUrl) _verifyUpload(config, stagingDir, s3BucketUrl) else: logger.debug("Encryption is enabled; files will be uploaded after being encrypted.") encryptedDir = tempfile.mkdtemp(dir=config.options.workingDir) changeOwnership(encryptedDir, config.options.backupUser, config.options.backupGroup) try: _encryptStagingDir(config, local, stagingDir, encryptedDir) _uploadStagingDir(config, encryptedDir, s3BucketUrl) _verifyUpload(config, encryptedDir, s3BucketUrl) finally: if os.path.exists(encryptedDir): shutil.rmtree(encryptedDir) ################################## # _writeStoreIndicator() function ################################## def _writeStoreIndicator(config, stagingDirs): """ Writes a store indicator file into staging directories. Args: config: Config object stagingDirs: Dictionary mapping directory path to date suffix """ for stagingDir in list(stagingDirs.keys()): writeIndicatorFile(stagingDir, STORE_INDICATOR, config.options.backupUser, config.options.backupGroup) ################################## # _clearExistingBackup() function ################################## def _clearExistingBackup(config, s3BucketUrl): """ Clear any existing backup files for an S3 bucket URL. Args: config: Config object s3BucketUrl: S3 bucket URL associated with the staging directory """ suCommand = resolveCommand(SU_COMMAND) awsCommand = resolveCommand(AWS_COMMAND) actualCommand = "%s s3 rm --recursive %s/" % (awsCommand[0], s3BucketUrl) result = executeCommand(suCommand, [config.options.backupUser, "-c", actualCommand])[0] if result != 0: raise IOError("Error [%d] calling AWS CLI to clear existing backup for [%s]." % (result, s3BucketUrl)) logger.debug("Completed clearing any existing backup in S3 for [%s]", s3BucketUrl) ############################### # _uploadStagingDir() function ############################### def _uploadStagingDir(config, stagingDir, s3BucketUrl): """ Upload the contents of a staging directory out to the Amazon S3 cloud. Args: config: Config object stagingDir: Staging directory to upload s3BucketUrl: S3 bucket URL associated with the staging directory """ # The version of awscli in Debian stretch (1.11.13-1) has a problem # uploading empty files, due to running with Python 3 rather than Python 2 # as the upstream maintainers intended. To work around this, I'm explicitly # excluding files like cback.stage, cback.collect, etc. which should be the # only empty files we ever try to copy. See: https://github.com/aws/aws-cli/issues/2403 suCommand = resolveCommand(SU_COMMAND) awsCommand = resolveCommand(AWS_COMMAND) actualCommand = '%s s3 cp --recursive --exclude "*cback.*" %s/ %s/' % (awsCommand[0], stagingDir, s3BucketUrl) result = executeCommand(suCommand, [config.options.backupUser, "-c", actualCommand])[0] if result != 0: raise IOError("Error [%d] calling AWS CLI to upload staging directory to [%s]." % (result, s3BucketUrl)) logger.debug("Completed uploading staging dir [%s] to [%s]", stagingDir, s3BucketUrl) ########################### # _verifyUpload() function ########################### def _verifyUpload(config, stagingDir, s3BucketUrl): """ Verify that a staging directory was properly uploaded to the Amazon S3 cloud. Args: config: Config object stagingDir: Staging directory to verify s3BucketUrl: S3 bucket URL associated with the staging directory """ (bucket, prefix) = s3BucketUrl.replace("s3://", "").split("/", 1) suCommand = resolveCommand(SU_COMMAND) awsCommand = resolveCommand(AWS_COMMAND) query = "Contents[].{Key: Key, Size: Size}" actualCommand = "%s s3api list-objects --bucket %s --prefix %s --query '%s'" % (awsCommand[0], bucket, prefix, query) (result, data) = executeCommand(suCommand, [config.options.backupUser, "-c", actualCommand], returnOutput=True) if result != 0: raise IOError("Error [%d] calling AWS CLI verify upload to [%s]." % (result, s3BucketUrl)) contents = {} for entry in json.loads("".join(data)): key = entry["Key"].replace(prefix, "") size = int(entry["Size"]) contents[key] = size files = FilesystemList() files.excludeBasenamePatterns = [r"cback\..*"] # because these are excluded from the upload files.addDirContents(stagingDir) for entry in files: if os.path.isfile(entry): key = entry.replace(stagingDir, "") size = int(os.stat(entry).st_size) if key not in contents: raise IOError("File was apparently not uploaded: [%s]" % entry) else: if size != contents[key]: raise IOError("File size differs [%s], expected %s bytes but got %s bytes" % (entry, size, contents[key])) logger.debug("Completed verifying upload from [%s] to [%s].", stagingDir, s3BucketUrl) ################################ # _encryptStagingDir() function ################################ def _encryptStagingDir(config, local, stagingDir, encryptedDir): """ Encrypt a staging directory, creating a new directory in the process. Args: config: Config object stagingDir: Staging directory to use as source encryptedDir: Target directory into which encrypted files should be written """ suCommand = resolveCommand(SU_COMMAND) files = FilesystemList() files.addDirContents(stagingDir) for cleartext in files: if os.path.isfile(cleartext): encrypted = "%s%s" % (encryptedDir, cleartext.replace(stagingDir, "")) if int(os.stat(cleartext).st_size) == 0: with open(encrypted, "a") as f: # pylint: disable=unspecified-encoding f.close() # don't bother encrypting empty files else: actualCommand = local.amazons3.encryptCommand.replace("${input}", cleartext).replace("${output}", encrypted) subdir = os.path.dirname(encrypted) if not os.path.isdir(subdir): os.makedirs(subdir) changeOwnership(subdir, config.options.backupUser, config.options.backupGroup) result = executeCommand(suCommand, [config.options.backupUser, "-c", actualCommand])[0] if result != 0: raise IOError("Error [%d] encrypting [%s]." % (result, cleartext)) logger.debug("Completed encrypting staging directory [%s] into [%s]", stagingDir, encryptedDir) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/extend/capacity.py0000644000000000000000000005305514567004737017640 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides an extension to check remaining media capacity. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides an extension to check remaining media capacity. Some users have asked for advance warning that their media is beginning to fill up. This is an extension that checks the current capacity of the media in the writer, and prints a warning if the media is more than X% full, or has fewer than X bytes of capacity remaining. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging from functools import total_ordering from CedarBackup3.actions.util import checkMediaState, createWriter from CedarBackup3.config import ByteQuantity, addByteQuantityNode, readByteQuantity from CedarBackup3.util import displayBytes from CedarBackup3.xmlutil import addContainerNode, addStringNode, createInputDom, readFirstChild, readString ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.capacity") ######################################################################## # Percentage class definition ######################################################################## @total_ordering class PercentageQuantity(object): """ Class representing a percentage quantity. The percentage is maintained internally as a string so that issues of precision can be avoided. It really isn't possible to store a floating point number here while being able to losslessly translate back and forth between XML and object representations. (Perhaps the Python 2.4 Decimal class would have been an option, but I originally wanted to stay compatible with Python 2.3.) Even though the quantity is maintained as a string, the string must be in a valid floating point positive number. Technically, any floating point string format supported by Python is allowble. However, it does not make sense to have a negative percentage in this context. """ def __init__(self, quantity=None): """ Constructor for the ``PercentageQuantity`` class. Args: quantity: Percentage quantity, as a string (i.e. "99.9" or "12") Raises: ValueError: If the quantity value is invaid """ self._quantity = None self.quantity = quantity def __repr__(self): """ Official string representation for class instance. """ return "PercentageQuantity(%s)" % (self.quantity) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.quantity != other.quantity: if float(self.quantity or 0.0) < float(other.quantity or 0.0): return -1 else: return 1 return 0 def _setQuantity(self, value): """ Property target used to set the quantity The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string ValueError: If the value is not a valid floating point number ValueError: If the value is less than zero """ if value is not None: if len(value) < 1: raise ValueError("Percentage must be a non-empty string.") floatValue = float(value) if floatValue < 0.0 or floatValue > 100.0: raise ValueError("Percentage must be a positive value from 0.0 to 100.0") self._quantity = value # keep around string def _getQuantity(self): """ Property target used to get the quantity. """ return self._quantity def _getPercentage(self): """ Property target used to get the quantity as a floating point number. If there is no quantity set, then a value of 0.0 is returned. """ if self.quantity is not None: return float(self.quantity) return 0.0 quantity = property(_getQuantity, _setQuantity, None, doc="Percentage value, as a string") percentage = property(_getPercentage, None, None, "Percentage value, as a floating point number.") ######################################################################## # CapacityConfig class definition ######################################################################## @total_ordering class CapacityConfig(object): """ Class representing capacity configuration. The following restrictions exist on data in this class: - The maximum percentage utilized must be a PercentageQuantity - The minimum bytes remaining must be a ByteQuantity """ def __init__(self, maxPercentage=None, minBytes=None): """ Constructor for the ``CapacityConfig`` class. Args: maxPercentage: Maximum percentage of the media that may be utilized minBytes: Minimum number of free bytes that must be available """ self._maxPercentage = None self._minBytes = None self.maxPercentage = maxPercentage self.minBytes = minBytes def __repr__(self): """ Official string representation for class instance. """ return "CapacityConfig(%s, %s)" % (self.maxPercentage, self.minBytes) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.maxPercentage != other.maxPercentage: if (self.maxPercentage or PercentageQuantity()) < (other.maxPercentage or PercentageQuantity()): return -1 else: return 1 if self.minBytes != other.minBytes: if (self.minBytes or ByteQuantity()) < (other.minBytes or ByteQuantity()): return -1 else: return 1 return 0 def _setMaxPercentage(self, value): """ Property target used to set the maxPercentage value. If not ``None``, the value must be a ``PercentageQuantity`` object. Raises: ValueError: If the value is not a ``PercentageQuantity`` """ if value is None: self._maxPercentage = None else: if not isinstance(value, PercentageQuantity): raise ValueError("Value must be a ``PercentageQuantity`` object.") self._maxPercentage = value def _getMaxPercentage(self): """ Property target used to get the maxPercentage value """ return self._maxPercentage def _setMinBytes(self, value): """ Property target used to set the bytes utilized value. If not ``None``, the value must be a ``ByteQuantity`` object. Raises: ValueError: If the value is not a ``ByteQuantity`` """ if value is None: self._minBytes = None else: if not isinstance(value, ByteQuantity): raise ValueError("Value must be a ``ByteQuantity`` object.") self._minBytes = value def _getMinBytes(self): """ Property target used to get the bytes remaining value. """ return self._minBytes maxPercentage = property(_getMaxPercentage, _setMaxPercentage, None, "Maximum percentage of the media that may be utilized.") minBytes = property(_getMinBytes, _setMinBytes, None, "Minimum number of free bytes that must be available.") ######################################################################## # LocalConfig class definition ######################################################################## @total_ordering class LocalConfig(object): """ Class representing this extension's configuration document. This is not a general-purpose configuration object like the main Cedar Backup configuration object. Instead, it just knows how to parse and emit specific configuration values to this extension. Third parties who need to read and write configuration related to this extension should access it through the constructor, ``validate`` and ``addConfig`` methods. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath`` then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._capacity = None self.capacity = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() def __repr__(self): """ Official string representation for class instance. """ return "LocalConfig(%s)" % (self.capacity) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.capacity != other.capacity: if self.capacity < other.capacity: return -1 else: return 1 return 0 def _setCapacity(self, value): """ Property target used to set the capacity configuration value. If not ``None``, the value must be a ``CapacityConfig`` object. Raises: ValueError: If the value is not a ``CapacityConfig`` """ if value is None: self._capacity = None else: if not isinstance(value, CapacityConfig): raise ValueError("Value must be a ``CapacityConfig`` object.") self._capacity = value def _getCapacity(self): """ Property target used to get the capacity configuration value. """ return self._capacity capacity = property(_getCapacity, _setCapacity, None, "Capacity configuration in terms of a ``CapacityConfig`` object.") def validate(self): """ Validates configuration represented by the object. THere must be either a percentage, or a byte capacity, but not both. Raises: ValueError: If one of the validations fails """ if self.capacity is None: raise ValueError("Capacity section is required.") if self.capacity.maxPercentage is None and self.capacity.minBytes is None: raise ValueError("Must provide either max percentage or min bytes.") if self.capacity.maxPercentage is not None and self.capacity.minBytes is not None: raise ValueError("Must provide either max percentage or min bytes, but not both.") def addConfig(self, xmlDom, parentNode): """ Adds a configuration section as the next child of a parent. Third parties should use this function to write configuration related to this extension. We add the following fields to the document:: maxPercentage //cb_config/capacity/max_percentage minBytes //cb_config/capacity/min_bytes Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to """ if self.capacity is not None: sectionNode = addContainerNode(xmlDom, parentNode, "capacity") LocalConfig._addPercentageQuantity(xmlDom, sectionNode, "max_percentage", self.capacity.maxPercentage) if self.capacity.minBytes is not None: # because utility function fills in empty section on None addByteQuantityNode(xmlDom, sectionNode, "min_bytes", self.capacity.minBytes) def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls a static method to parse the capacity configuration section. Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._capacity = LocalConfig._parseCapacity(parentNode) @staticmethod def _parseCapacity(parentNode): """ Parses a capacity configuration section. We read the following fields:: maxPercentage //cb_config/capacity/max_percentage minBytes //cb_config/capacity/min_bytes Args: parentNode: Parent node to search beneath Returns: ``CapacityConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ capacity = None section = readFirstChild(parentNode, "capacity") if section is not None: capacity = CapacityConfig() capacity.maxPercentage = LocalConfig._readPercentageQuantity(section, "max_percentage") capacity.minBytes = readByteQuantity(section, "min_bytes") return capacity @staticmethod def _readPercentageQuantity(parent, name): """ Read a percentage quantity value from an XML document. Args: parent: Parent node to search beneath name: Name of node to search for Returns: Percentage quantity parsed from XML document """ quantity = readString(parent, name) if quantity is None: return None return PercentageQuantity(quantity) @staticmethod def _addPercentageQuantity(xmlDom, parentNode, nodeName, percentageQuantity): """ Adds a text node as the next child of a parent, to contain a percentage quantity. If the ``percentageQuantity`` is None, then no node will be created. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent node to create child for nodeName: Name of the new container node percentageQuantity: PercentageQuantity object to put into the XML document Returns: Reference to the newly-created node """ if percentageQuantity is not None: addStringNode(xmlDom, parentNode, nodeName, percentageQuantity.quantity) ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### # pylint: disable=W0613 def executeAction(configPath, options, config): """ Executes the capacity action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If there are I/O problems reading or writing files """ logger.debug("Executing capacity extended action.") if config.options is None or config.store is None: raise ValueError("Cedar Backup configuration is not properly filled in.") local = LocalConfig(xmlPath=configPath) if config.store.checkMedia: checkMediaState(config.store) # raises exception if media is not initialized capacity = createWriter(config).retrieveCapacity() logger.debug("Media capacity: %s", capacity) if local.capacity.maxPercentage is not None: if capacity.utilized > local.capacity.maxPercentage.percentage: logger.error( "Media has reached capacity limit of %s%%: %.2f%% utilized", local.capacity.maxPercentage.quantity, capacity.utilized, ) else: if capacity.bytesAvailable < local.capacity.minBytes: logger.error( "Media has reached capacity limit of %s: only %s available", local.capacity.minBytes, displayBytes(capacity.bytesAvailable), ) logger.info("Executed the capacity extended action successfully.") ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/extend/encrypt.py0000644000000000000000000005161214567004737017524 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides an extension to encrypt staging directories. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides an extension to encrypt staging directories. When this extension is executed, all backed-up files in the configured Cedar Backup staging directory will be encrypted using gpg. Any directory which has already been encrypted (as indicated by the ``cback.encrypt`` file) will be ignored. This extension requires a new configuration section and is intended to be run immediately after the standard stage action or immediately before the standard store action. Aside from its own configuration, it requires the options and staging configuration sections in the standard Cedar Backup configuration file. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os from functools import total_ordering from CedarBackup3.actions.util import findDailyDirs, getBackupFiles, writeIndicatorFile from CedarBackup3.util import changeOwnership, executeCommand, resolveCommand from CedarBackup3.xmlutil import addContainerNode, addStringNode, createInputDom, readFirstChild, readString ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.encrypt") GPG_COMMAND = [ "gpg", ] VALID_ENCRYPT_MODES = [ "gpg", ] ENCRYPT_INDICATOR = "cback.encrypt" ######################################################################## # EncryptConfig class definition ######################################################################## @total_ordering class EncryptConfig(object): """ Class representing encrypt configuration. Encrypt configuration is used for encrypting staging directories. The following restrictions exist on data in this class: - The encrypt mode must be one of the values in ``VALID_ENCRYPT_MODES`` - The encrypt target value must be a non-empty string """ def __init__(self, encryptMode=None, encryptTarget=None): """ Constructor for the ``EncryptConfig`` class. Args: encryptMode: Encryption mode encryptTarget: Encryption target (for instance, GPG recipient) Raises: ValueError: If one of the values is invalid """ self._encryptMode = None self._encryptTarget = None self.encryptMode = encryptMode self.encryptTarget = encryptTarget def __repr__(self): """ Official string representation for class instance. """ return "EncryptConfig(%s, %s)" % (self.encryptMode, self.encryptTarget) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.encryptMode != other.encryptMode: if str(self.encryptMode or "") < str(other.encryptMode or ""): return -1 else: return 1 if self.encryptTarget != other.encryptTarget: if str(self.encryptTarget or "") < str(other.encryptTarget or ""): return -1 else: return 1 return 0 def _setEncryptMode(self, value): """ Property target used to set the encrypt mode. If not ``None``, the mode must be one of the values in ``VALID_ENCRYPT_MODES``. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_ENCRYPT_MODES: raise ValueError("Encrypt mode must be one of %s." % VALID_ENCRYPT_MODES) self._encryptMode = value def _getEncryptMode(self): """ Property target used to get the encrypt mode. """ return self._encryptMode def _setEncryptTarget(self, value): """ Property target used to set the encrypt target. """ if value is not None: if len(value) < 1: raise ValueError("Encrypt target must be non-empty string.") self._encryptTarget = value def _getEncryptTarget(self): """ Property target used to get the encrypt target. """ return self._encryptTarget encryptMode = property(_getEncryptMode, _setEncryptMode, None, doc="Encrypt mode.") encryptTarget = property(_getEncryptTarget, _setEncryptTarget, None, doc="Encrypt target (i.e. GPG recipient).") ######################################################################## # LocalConfig class definition ######################################################################## @total_ordering class LocalConfig(object): """ Class representing this extension's configuration document. This is not a general-purpose configuration object like the main Cedar Backup configuration object. Instead, it just knows how to parse and emit encrypt-specific configuration values. Third parties who need to read and write configuration related to this extension should access it through the constructor, ``validate`` and ``addConfig`` methods. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath`` then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._encrypt = None self.encrypt = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() def __repr__(self): """ Official string representation for class instance. """ return "LocalConfig(%s)" % (self.encrypt) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.encrypt != other.encrypt: if self.encrypt < other.encrypt: return -1 else: return 1 return 0 def _setEncrypt(self, value): """ Property target used to set the encrypt configuration value. If not ``None``, the value must be a ``EncryptConfig`` object. Raises: ValueError: If the value is not a ``EncryptConfig`` """ if value is None: self._encrypt = None else: if not isinstance(value, EncryptConfig): raise ValueError("Value must be a ``EncryptConfig`` object.") self._encrypt = value def _getEncrypt(self): """ Property target used to get the encrypt configuration value. """ return self._encrypt encrypt = property(_getEncrypt, _setEncrypt, None, "Encrypt configuration in terms of a ``EncryptConfig`` object.") def validate(self): """ Validates configuration represented by the object. Encrypt configuration must be filled in. Within that, both the encrypt mode and encrypt target must be filled in. Raises: ValueError: If one of the validations fails """ if self.encrypt is None: raise ValueError("Encrypt section is required.") if self.encrypt.encryptMode is None: raise ValueError("Encrypt mode must be set.") if self.encrypt.encryptTarget is None: raise ValueError("Encrypt target must be set.") def addConfig(self, xmlDom, parentNode): """ Adds an configuration section as the next child of a parent. Third parties should use this function to write configuration related to this extension. We add the following fields to the document:: encryptMode //cb_config/encrypt/encrypt_mode encryptTarget //cb_config/encrypt/encrypt_target Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to """ if self.encrypt is not None: sectionNode = addContainerNode(xmlDom, parentNode, "encrypt") addStringNode(xmlDom, sectionNode, "encrypt_mode", self.encrypt.encryptMode) addStringNode(xmlDom, sectionNode, "encrypt_target", self.encrypt.encryptTarget) def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls a static method to parse the encrypt configuration section. Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._encrypt = LocalConfig._parseEncrypt(parentNode) @staticmethod def _parseEncrypt(parent): """ Parses an encrypt configuration section. We read the following individual fields:: encryptMode //cb_config/encrypt/encrypt_mode encryptTarget //cb_config/encrypt/encrypt_target Args: parent: Parent node to search beneath Returns: ``EncryptConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ encrypt = None section = readFirstChild(parent, "encrypt") if section is not None: encrypt = EncryptConfig() encrypt.encryptMode = readString(section, "encrypt_mode") encrypt.encryptTarget = readString(section, "encrypt_target") return encrypt ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### # pylint: disable=W0613 def executeAction(configPath, options, config): """ Executes the encrypt backup action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If there are I/O problems reading or writing files """ logger.debug("Executing encrypt extended action.") if config.options is None or config.stage is None: raise ValueError("Cedar Backup configuration is not properly filled in.") local = LocalConfig(xmlPath=configPath) if local.encrypt.encryptMode not in ["gpg"]: raise ValueError("Unknown encrypt mode [%s]" % local.encrypt.encryptMode) if local.encrypt.encryptMode == "gpg": _confirmGpgRecipient(local.encrypt.encryptTarget) dailyDirs = findDailyDirs(config.stage.targetDir, ENCRYPT_INDICATOR) for dailyDir in dailyDirs: _encryptDailyDir( dailyDir, local.encrypt.encryptMode, local.encrypt.encryptTarget, config.options.backupUser, config.options.backupGroup ) writeIndicatorFile(dailyDir, ENCRYPT_INDICATOR, config.options.backupUser, config.options.backupGroup) logger.info("Executed the encrypt extended action successfully.") ############################## # _encryptDailyDir() function ############################## def _encryptDailyDir(dailyDir, encryptMode, encryptTarget, backupUser, backupGroup): """ Encrypts the contents of a daily staging directory. Indicator files are ignored. All other files are encrypted. The only valid encrypt mode is ``"gpg"``. Args: dailyDir: Daily directory to encrypt encryptMode: Encryption mode (only "gpg" is allowed) encryptTarget: Encryption target (GPG recipient for "gpg" mode) backupUser: User that target files should be owned by backupGroup: Group that target files should be owned by Raises: ValueError: If the encrypt mode is not supported ValueError: If the daily staging directory does not exist """ logger.debug("Begin encrypting contents of [%s].", dailyDir) fileList = getBackupFiles(dailyDir) # ignores indicator files for path in fileList: _encryptFile(path, encryptMode, encryptTarget, backupUser, backupGroup, removeSource=True) logger.debug("Completed encrypting contents of [%s].", dailyDir) ########################## # _encryptFile() function ########################## def _encryptFile(sourcePath, encryptMode, encryptTarget, backupUser, backupGroup, removeSource=False): """ Encrypts the source file using the indicated mode. The encrypted file will be owned by the indicated backup user and group. If ``removeSource`` is ``True``, then the source file will be removed after it is successfully encrypted. Currently, only the ``"gpg"`` encrypt mode is supported. Args: sourcePath: Absolute path of the source file to encrypt encryptMode: Encryption mode (only "gpg" is allowed) encryptTarget: Encryption target (GPG recipient) backupUser: User that target files should be owned by backupGroup: Group that target files should be owned by removeSource: Indicates whether to remove the source file Returns: Path to the newly-created encrypted file Raises: ValueError: If an invalid encrypt mode is passed in IOError: If there is a problem accessing, encrypting or removing the source file """ if not os.path.exists(sourcePath): raise ValueError("Source path [%s] does not exist." % sourcePath) if encryptMode == "gpg": encryptedPath = _encryptFileWithGpg(sourcePath, recipient=encryptTarget) else: raise ValueError("Unknown encrypt mode [%s]" % encryptMode) changeOwnership(encryptedPath, backupUser, backupGroup) if removeSource: if os.path.exists(sourcePath): try: os.remove(sourcePath) logger.debug("Completed removing old file [%s].", sourcePath) except: raise IOError("Failed to remove file [%s] after encrypting it." % (sourcePath)) return encryptedPath ################################# # _encryptFileWithGpg() function ################################# def _encryptFileWithGpg(sourcePath, recipient): """ Encrypts the indicated source file using GPG. The encrypted file will be in GPG's binary output format and will have the same name as the source file plus a ``".gpg"`` extension. The source file will not be modified or removed by this function call. Args: sourcePath: Absolute path of file to be encrypted recipient: Recipient name to be passed to GPG's ``"-r"`` option Returns: Path to the newly-created encrypted file Raises: IOError: If there is a problem encrypting the file """ encryptedPath = "%s.gpg" % sourcePath command = resolveCommand(GPG_COMMAND) args = ["--batch", "--yes", "-e", "-r", recipient, "-o", encryptedPath, sourcePath] result = executeCommand(command, args)[0] if result != 0: raise IOError("Error [%d] calling gpg to encrypt [%s]." % (result, sourcePath)) if not os.path.exists(encryptedPath): raise IOError("After call to [%s], encrypted file [%s] does not exist." % (command, encryptedPath)) logger.debug("Completed encrypting file [%s] to [%s].", sourcePath, encryptedPath) return encryptedPath ################################# # _confirmGpgRecpient() function ################################# def _confirmGpgRecipient(recipient): """ Confirms that a recipient's public key is known to GPG. Throws an exception if there is a problem, or returns normally otherwise. Args: recipient: Recipient name Raises: IOError: If the recipient's public key is not known to GPG """ command = resolveCommand(GPG_COMMAND) args = ["--batch", "-k", recipient] # should use --with-colons if the output will be parsed result = executeCommand(command, args)[0] if result != 0: raise IOError("GPG unable to find public key for [%s]." % recipient) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/extend/mbox.py0000644000000000000000000016440314567004737017010 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2006-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides an extension to back up mbox email files. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides an extension to back up mbox email files. Backing up email ================ Email folders (often stored as mbox flatfiles) are not well-suited being backed up with an incremental backup like the one offered by Cedar Backup. This is because mbox files often change on a daily basis, forcing the incremental backup process to back them up every day in order to avoid losing data. This can result in quite a bit of wasted space when backing up large folders. (Note that the alternative maildir format does not share this problem, since it typically uses one file per message.) One solution to this problem is to design a smarter incremental backup process, which backs up baseline content on the first day of the week, and then backs up only new messages added to that folder on every other day of the week. This way, the backup for any single day is only as large as the messages placed into the folder on that day. The backup isn't as "perfect" as the incremental backup process, because it doesn't preserve information about messages deleted from the backed-up folder. However, it should be much more space-efficient, and in a recovery situation, it seems better to restore too much data rather than too little. What is this extension? ======================= This is a Cedar Backup extension used to back up mbox email files via the Cedar Backup command line. Individual mbox files or directories containing mbox files can be backed up using the same collect modes allowed for filesystems in the standard Cedar Backup collect action: weekly, daily, incremental. It implements the "smart" incremental backup process discussed above, using functionality provided by the ``grepmail`` utility. This extension requires a new configuration section and is intended to be run either immediately before or immediately after the standard collect action. Aside from its own configuration, it requires the options and collect configuration sections in the standard Cedar Backup configuration file. The mbox action is conceptually similar to the standard collect action, except that mbox directories are not collected recursively. This implies some configuration changes (i.e. there's no need for global exclusions or an ignore file). If you back up a directory, all of the mbox files in that directory are backed up into a single tar file using the indicated compression method. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import datetime import logging import os import pickle import tempfile from bz2 import BZ2File from functools import total_ordering from gzip import GzipFile from CedarBackup3.config import VALID_COLLECT_MODES, VALID_COMPRESS_MODES from CedarBackup3.filesystem import BackupFileList, FilesystemList from CedarBackup3.util import ( ObjectTypeList, RegexList, UnorderedList, buildNormalizedPath, changeOwnership, encodePath, executeCommand, isStartOfWeek, pathJoin, resolveCommand, ) from CedarBackup3.xmlutil import ( addContainerNode, addStringNode, createInputDom, isElement, readChildren, readFirstChild, readString, readStringList, ) ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.mbox") GREPMAIL_COMMAND = ["grepmail"] REVISION_PATH_EXTENSION = "mboxlast" ######################################################################## # MboxFile class definition ######################################################################## @total_ordering class MboxFile(object): """ Class representing mbox file configuration.. The following restrictions exist on data in this class: - The absolute path must be absolute. - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`. """ def __init__(self, absolutePath=None, collectMode=None, compressMode=None): """ Constructor for the ``MboxFile`` class. You should never directly instantiate this class. Args: absolutePath: Absolute path to an mbox file on disk collectMode: Overridden collect mode for this directory compressMode: Overridden compression mode for this directory """ self._absolutePath = None self._collectMode = None self._compressMode = None self.absolutePath = absolutePath self.collectMode = collectMode self.compressMode = compressMode def __repr__(self): """ Official string representation for class instance. """ return "MboxFile(%s, %s, %s)" % (self.absolutePath, self.collectMode, self.compressMode) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.absolutePath != other.absolutePath: if str(self.absolutePath or "") < str(other.absolutePath or ""): return -1 else: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.compressMode != other.compressMode: if str(self.compressMode or "") < str(other.compressMode or ""): return -1 else: return 1 return 0 def _setAbsolutePath(self, value): """ Property target used to set the absolute path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Absolute path must be, er, an absolute path.") self._absolutePath = encodePath(value) def _getAbsolutePath(self): """ Property target used to get the absolute path. """ return self._absolutePath def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of the values in :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setCompressMode(self, value): """ Property target used to set the compress mode. If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COMPRESS_MODES: raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) self._compressMode = value def _getCompressMode(self): """ Property target used to get the compress mode. """ return self._compressMode absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, doc="Absolute path to the mbox file.") collectMode = property(_getCollectMode, _setCollectMode, None, doc="Overridden collect mode for this mbox file.") compressMode = property(_getCompressMode, _setCompressMode, None, doc="Overridden compress mode for this mbox file.") ######################################################################## # MboxDir class definition ######################################################################## @total_ordering class MboxDir(object): """ Class representing mbox directory configuration.. The following restrictions exist on data in this class: - The absolute path must be absolute. - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Unlike collect directory configuration, this is the only place exclusions are allowed (no global exclusions at the configuration level). Also, we only allow relative exclusions and there is no configured ignore file. This is because mbox directory backups are not recursive. """ def __init__(self, absolutePath=None, collectMode=None, compressMode=None, relativeExcludePaths=None, excludePatterns=None): """ Constructor for the ``MboxDir`` class. You should never directly instantiate this class. Args: absolutePath: Absolute path to a mbox file on disk collectMode: Overridden collect mode for this directory compressMode: Overridden compression mode for this directory relativeExcludePaths: List of relative paths to exclude excludePatterns: List of regular expression patterns to exclude """ self._absolutePath = None self._collectMode = None self._compressMode = None self._relativeExcludePaths = None self._excludePatterns = None self.absolutePath = absolutePath self.collectMode = collectMode self.compressMode = compressMode self.relativeExcludePaths = relativeExcludePaths self.excludePatterns = excludePatterns def __repr__(self): """ Official string representation for class instance. """ return "MboxDir(%s, %s, %s, %s, %s)" % ( self.absolutePath, self.collectMode, self.compressMode, self.relativeExcludePaths, self.excludePatterns, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.absolutePath != other.absolutePath: if str(self.absolutePath or "") < str(other.absolutePath or ""): return -1 else: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.compressMode != other.compressMode: if str(self.compressMode or "") < str(other.compressMode or ""): return -1 else: return 1 if self.relativeExcludePaths != other.relativeExcludePaths: if self.relativeExcludePaths < other.relativeExcludePaths: return -1 else: return 1 if self.excludePatterns != other.excludePatterns: if self.excludePatterns < other.excludePatterns: return -1 else: return 1 return 0 def _setAbsolutePath(self, value): """ Property target used to set the absolute path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Absolute path must be, er, an absolute path.") self._absolutePath = encodePath(value) def _getAbsolutePath(self): """ Property target used to get the absolute path. """ return self._absolutePath def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of the values in :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setCompressMode(self, value): """ Property target used to set the compress mode. If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COMPRESS_MODES: raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) self._compressMode = value def _getCompressMode(self): """ Property target used to get the compress mode. """ return self._compressMode def _setRelativeExcludePaths(self, value): """ Property target used to set the relative exclude paths list. Elements do not have to exist on disk at the time of assignment. """ if value is None: self._relativeExcludePaths = None else: try: saved = self._relativeExcludePaths self._relativeExcludePaths = UnorderedList() self._relativeExcludePaths.extend(value) except Exception as e: self._relativeExcludePaths = saved raise e def _getRelativeExcludePaths(self): """ Property target used to get the relative exclude paths list. """ return self._relativeExcludePaths def _setExcludePatterns(self, value): """ Property target used to set the exclude patterns list. """ if value is None: self._excludePatterns = None else: try: saved = self._excludePatterns self._excludePatterns = RegexList() self._excludePatterns.extend(value) except Exception as e: self._excludePatterns = saved raise e def _getExcludePatterns(self): """ Property target used to get the exclude patterns list. """ return self._excludePatterns absolutePath = property(_getAbsolutePath, _setAbsolutePath, None, doc="Absolute path to the mbox directory.") collectMode = property(_getCollectMode, _setCollectMode, None, doc="Overridden collect mode for this mbox directory.") compressMode = property(_getCompressMode, _setCompressMode, None, doc="Overridden compress mode for this mbox directory.") relativeExcludePaths = property(_getRelativeExcludePaths, _setRelativeExcludePaths, None, "List of relative paths to exclude.") excludePatterns = property(_getExcludePatterns, _setExcludePatterns, None, "List of regular expression patterns to exclude.") ######################################################################## # MboxConfig class definition ######################################################################## @total_ordering class MboxConfig(object): """ Class representing mbox configuration. Mbox configuration is used for backing up mbox email files. The following restrictions exist on data in this class: - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`. - The ``mboxFiles`` list must be a list of ``MboxFile`` objects - The ``mboxDirs`` list must be a list of ``MboxDir`` objects For the ``mboxFiles`` and ``mboxDirs`` lists, validation is accomplished through the :any:`util.ObjectTypeList` list implementation that overrides common list methods and transparently ensures that each element is of the proper type. Unlike collect configuration, no global exclusions are allowed on this level. We only allow relative exclusions at the mbox directory level. Also, there is no configured ignore file. This is because mbox directory backups are not recursive. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, collectMode=None, compressMode=None, mboxFiles=None, mboxDirs=None): """ Constructor for the ``MboxConfig`` class. Args: collectMode: Default collect mode compressMode: Default compress mode mboxFiles: List of mbox files to back up mboxDirs: List of mbox directories to back up Raises: ValueError: If one of the values is invalid """ self._collectMode = None self._compressMode = None self._mboxFiles = None self._mboxDirs = None self.collectMode = collectMode self.compressMode = compressMode self.mboxFiles = mboxFiles self.mboxDirs = mboxDirs def __repr__(self): """ Official string representation for class instance. """ return "MboxConfig(%s, %s, %s, %s)" % (self.collectMode, self.compressMode, self.mboxFiles, self.mboxDirs) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.compressMode != other.compressMode: if str(self.compressMode or "") < str(other.compressMode or ""): return -1 else: return 1 if self.mboxFiles != other.mboxFiles: if self.mboxFiles < other.mboxFiles: return -1 else: return 1 if self.mboxDirs != other.mboxDirs: if self.mboxDirs < other.mboxDirs: return -1 else: return 1 return 0 def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of the values in :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setCompressMode(self, value): """ Property target used to set the compress mode. If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COMPRESS_MODES: raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) self._compressMode = value def _getCompressMode(self): """ Property target used to get the compress mode. """ return self._compressMode def _setMboxFiles(self, value): """ Property target used to set the mboxFiles list. Either the value must be ``None`` or each element must be an ``MboxFile``. Raises: ValueError: If the value is not an ``MboxFile`` """ if value is None: self._mboxFiles = None else: try: saved = self._mboxFiles self._mboxFiles = ObjectTypeList(MboxFile, "MboxFile") self._mboxFiles.extend(value) except Exception as e: self._mboxFiles = saved raise e def _getMboxFiles(self): """ Property target used to get the mboxFiles list. """ return self._mboxFiles def _setMboxDirs(self, value): """ Property target used to set the mboxDirs list. Either the value must be ``None`` or each element must be an ``MboxDir``. Raises: ValueError: If the value is not an ``MboxDir`` """ if value is None: self._mboxDirs = None else: try: saved = self._mboxDirs self._mboxDirs = ObjectTypeList(MboxDir, "MboxDir") self._mboxDirs.extend(value) except Exception as e: self._mboxDirs = saved raise e def _getMboxDirs(self): """ Property target used to get the mboxDirs list. """ return self._mboxDirs collectMode = property(_getCollectMode, _setCollectMode, None, doc="Default collect mode.") compressMode = property(_getCompressMode, _setCompressMode, None, doc="Default compress mode.") mboxFiles = property(_getMboxFiles, _setMboxFiles, None, doc="List of mbox files to back up.") mboxDirs = property(_getMboxDirs, _setMboxDirs, None, doc="List of mbox directories to back up.") ######################################################################## # LocalConfig class definition ######################################################################## @total_ordering class LocalConfig(object): """ Class representing this extension's configuration document. This is not a general-purpose configuration object like the main Cedar Backup configuration object. Instead, it just knows how to parse and emit Mbox-specific configuration values. Third parties who need to read and write configuration related to this extension should access it through the constructor, ``validate`` and ``addConfig`` methods. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath`` then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._mbox = None self.mbox = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() def __repr__(self): """ Official string representation for class instance. """ return "LocalConfig(%s)" % (self.mbox) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.mbox != other.mbox: if self.mbox < other.mbox: return -1 else: return 1 return 0 def _setMbox(self, value): """ Property target used to set the mbox configuration value. If not ``None``, the value must be a ``MboxConfig`` object. Raises: ValueError: If the value is not a ``MboxConfig`` """ if value is None: self._mbox = None else: if not isinstance(value, MboxConfig): raise ValueError("Value must be a ``MboxConfig`` object.") self._mbox = value def _getMbox(self): """ Property target used to get the mbox configuration value. """ return self._mbox mbox = property(_getMbox, _setMbox, None, "Mbox configuration in terms of a ``MboxConfig`` object.") def validate(self): """ Validates configuration represented by the object. Mbox configuration must be filled in. Within that, the collect mode and compress mode are both optional, but the list of repositories must contain at least one entry. Each configured file or directory must contain an absolute path, and then must be either able to take collect mode and compress mode configuration from the parent ``MboxConfig`` object, or must set each value on its own. Raises: ValueError: If one of the validations fails """ if self.mbox is None: raise ValueError("Mbox section is required.") if (self.mbox.mboxFiles is None or len(self.mbox.mboxFiles) < 1) and ( self.mbox.mboxDirs is None or len(self.mbox.mboxDirs) < 1 ): raise ValueError("At least one mbox file or directory must be configured.") if self.mbox.mboxFiles is not None: for mboxFile in self.mbox.mboxFiles: if mboxFile.absolutePath is None: raise ValueError("Each mbox file must set an absolute path.") if self.mbox.collectMode is None and mboxFile.collectMode is None: raise ValueError("Collect mode must either be set in parent mbox section or individual mbox file.") if self.mbox.compressMode is None and mboxFile.compressMode is None: raise ValueError("Compress mode must either be set in parent mbox section or individual mbox file.") if self.mbox.mboxDirs is not None: for mboxDir in self.mbox.mboxDirs: if mboxDir.absolutePath is None: raise ValueError("Each mbox directory must set an absolute path.") if self.mbox.collectMode is None and mboxDir.collectMode is None: raise ValueError("Collect mode must either be set in parent mbox section or individual mbox directory.") if self.mbox.compressMode is None and mboxDir.compressMode is None: raise ValueError("Compress mode must either be set in parent mbox section or individual mbox directory.") def addConfig(self, xmlDom, parentNode): """ Adds an configuration section as the next child of a parent. Third parties should use this function to write configuration related to this extension. We add the following fields to the document:: collectMode //cb_config/mbox/collectMode compressMode //cb_config/mbox/compressMode We also add groups of the following items, one list element per item:: mboxFiles //cb_config/mbox/file mboxDirs //cb_config/mbox/dir The mbox files and mbox directories are added by ``_addMboxFile`` and ``_addMboxDir``. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to """ if self.mbox is not None: sectionNode = addContainerNode(xmlDom, parentNode, "mbox") addStringNode(xmlDom, sectionNode, "collect_mode", self.mbox.collectMode) addStringNode(xmlDom, sectionNode, "compress_mode", self.mbox.compressMode) if self.mbox.mboxFiles is not None: for mboxFile in self.mbox.mboxFiles: LocalConfig._addMboxFile(xmlDom, sectionNode, mboxFile) if self.mbox.mboxDirs is not None: for mboxDir in self.mbox.mboxDirs: LocalConfig._addMboxDir(xmlDom, sectionNode, mboxDir) def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls a static method to parse the mbox configuration section. Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._mbox = LocalConfig._parseMbox(parentNode) @staticmethod def _parseMbox(parent): """ Parses an mbox configuration section. We read the following individual fields:: collectMode //cb_config/mbox/collect_mode compressMode //cb_config/mbox/compress_mode We also read groups of the following item, one list element per item:: mboxFiles //cb_config/mbox/file mboxDirs //cb_config/mbox/dir The mbox files are parsed by :any:`_parseMboxFiles` and the mbox directories are parsed by :any:`_parseMboxDirs`. Args: parent: Parent node to search beneath Returns: ``MboxConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ mbox = None section = readFirstChild(parent, "mbox") if section is not None: mbox = MboxConfig() mbox.collectMode = readString(section, "collect_mode") mbox.compressMode = readString(section, "compress_mode") mbox.mboxFiles = LocalConfig._parseMboxFiles(section) mbox.mboxDirs = LocalConfig._parseMboxDirs(section) return mbox @staticmethod def _parseMboxFiles(parent): """ Reads a list of ``MboxFile`` objects from immediately beneath the parent. We read the following individual fields:: absolutePath abs_path collectMode collect_mode compressMode compess_mode Args: parent: Parent node to search beneath Returns: List of ``MboxFile`` objects or ``None`` if none are found Raises: ValueError: If some filled-in value is invalid """ lst = [] for entry in readChildren(parent, "file"): if isElement(entry): mboxFile = MboxFile() mboxFile.absolutePath = readString(entry, "abs_path") mboxFile.collectMode = readString(entry, "collect_mode") mboxFile.compressMode = readString(entry, "compress_mode") lst.append(mboxFile) if not lst: lst = None return lst @staticmethod def _parseMboxDirs(parent): """ Reads a list of ``MboxDir`` objects from immediately beneath the parent. We read the following individual fields:: absolutePath abs_path collectMode collect_mode compressMode compess_mode We also read groups of the following items, one list element per item:: relativeExcludePaths exclude/rel_path excludePatterns exclude/pattern The exclusions are parsed by :any:`_parseExclusions`. Args: parent: Parent node to search beneath Returns: List of ``MboxDir`` objects or ``None`` if none are found Raises: ValueError: If some filled-in value is invalid """ lst = [] for entry in readChildren(parent, "dir"): if isElement(entry): mboxDir = MboxDir() mboxDir.absolutePath = readString(entry, "abs_path") mboxDir.collectMode = readString(entry, "collect_mode") mboxDir.compressMode = readString(entry, "compress_mode") (mboxDir.relativeExcludePaths, mboxDir.excludePatterns) = LocalConfig._parseExclusions(entry) lst.append(mboxDir) if not lst: lst = None return lst @staticmethod def _parseExclusions(parentNode): """ Reads exclusions data from immediately beneath the parent. We read groups of the following items, one list element per item:: relative exclude/rel_path patterns exclude/pattern If there are none of some pattern (i.e. no relative path items) then ``None`` will be returned for that item in the tuple. Args: parentNode: Parent node to search beneath Returns: Tuple of (relative, patterns) exclusions """ section = readFirstChild(parentNode, "exclude") if section is None: return (None, None) else: relative = readStringList(section, "rel_path") patterns = readStringList(section, "pattern") return (relative, patterns) @staticmethod def _addMboxFile(xmlDom, parentNode, mboxFile): """ Adds an mbox file container as the next child of a parent. We add the following fields to the document:: absolutePath file/abs_path collectMode file/collect_mode compressMode file/compress_mode The node itself is created as the next child of the parent node. This method only adds one mbox file node. The parent must loop for each mbox file in the ``MboxConfig`` object. If ``mboxFile`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to mboxFile: MboxFile to be added to the document """ if mboxFile is not None: sectionNode = addContainerNode(xmlDom, parentNode, "file") addStringNode(xmlDom, sectionNode, "abs_path", mboxFile.absolutePath) addStringNode(xmlDom, sectionNode, "collect_mode", mboxFile.collectMode) addStringNode(xmlDom, sectionNode, "compress_mode", mboxFile.compressMode) @staticmethod def _addMboxDir(xmlDom, parentNode, mboxDir): """ Adds an mbox directory container as the next child of a parent. We add the following fields to the document:: absolutePath dir/abs_path collectMode dir/collect_mode compressMode dir/compress_mode We also add groups of the following items, one list element per item:: relativeExcludePaths dir/exclude/rel_path excludePatterns dir/exclude/pattern The node itself is created as the next child of the parent node. This method only adds one mbox directory node. The parent must loop for each mbox directory in the ``MboxConfig`` object. If ``mboxDir`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to mboxDir: MboxDir to be added to the document """ if mboxDir is not None: sectionNode = addContainerNode(xmlDom, parentNode, "dir") addStringNode(xmlDom, sectionNode, "abs_path", mboxDir.absolutePath) addStringNode(xmlDom, sectionNode, "collect_mode", mboxDir.collectMode) addStringNode(xmlDom, sectionNode, "compress_mode", mboxDir.compressMode) if (mboxDir.relativeExcludePaths is not None and mboxDir.relativeExcludePaths != []) or ( mboxDir.excludePatterns is not None and mboxDir.excludePatterns != [] ): excludeNode = addContainerNode(xmlDom, sectionNode, "exclude") if mboxDir.relativeExcludePaths is not None: for relativePath in mboxDir.relativeExcludePaths: addStringNode(xmlDom, excludeNode, "rel_path", relativePath) if mboxDir.excludePatterns is not None: for pattern in mboxDir.excludePatterns: addStringNode(xmlDom, excludeNode, "pattern", pattern) ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### def executeAction(configPath, options, config): """ Executes the mbox backup action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If a backup could not be written for some reason """ logger.debug("Executing mbox extended action.") newRevision = datetime.datetime.today() # mark here so all actions are after this date/time if config.options is None or config.collect is None: raise ValueError("Cedar Backup configuration is not properly filled in.") local = LocalConfig(xmlPath=configPath) todayIsStart = isStartOfWeek(config.options.startingDay) fullBackup = options.full or todayIsStart logger.debug("Full backup flag is [%s]", fullBackup) if local.mbox.mboxFiles is not None: for mboxFile in local.mbox.mboxFiles: logger.debug("Working with mbox file [%s]", mboxFile.absolutePath) collectMode = _getCollectMode(local, mboxFile) compressMode = _getCompressMode(local, mboxFile) lastRevision = _loadLastRevision(config, mboxFile, fullBackup, collectMode) if fullBackup or (collectMode in ["daily", "incr"]) or (collectMode == "weekly" and todayIsStart): logger.debug("Mbox file meets criteria to be backed up today.") _backupMboxFile(config, mboxFile.absolutePath, fullBackup, collectMode, compressMode, lastRevision, newRevision) else: logger.debug("Mbox file will not be backed up, per collect mode.") if collectMode == "incr": _writeNewRevision(config, mboxFile, newRevision) if local.mbox.mboxDirs is not None: for mboxDir in local.mbox.mboxDirs: logger.debug("Working with mbox directory [%s]", mboxDir.absolutePath) collectMode = _getCollectMode(local, mboxDir) compressMode = _getCompressMode(local, mboxDir) lastRevision = _loadLastRevision(config, mboxDir, fullBackup, collectMode) (excludePaths, excludePatterns) = _getExclusions(mboxDir) if fullBackup or (collectMode in ["daily", "incr"]) or (collectMode == "weekly" and todayIsStart): logger.debug("Mbox directory meets criteria to be backed up today.") _backupMboxDir( config, mboxDir.absolutePath, fullBackup, collectMode, compressMode, lastRevision, newRevision, excludePaths, excludePatterns, ) else: logger.debug("Mbox directory will not be backed up, per collect mode.") if collectMode == "incr": _writeNewRevision(config, mboxDir, newRevision) logger.info("Executed the mbox extended action successfully.") def _getCollectMode(local, item): """ Gets the collect mode that should be used for an mbox file or directory. Use file- or directory-specific value if possible, otherwise take from mbox section. Args: local: LocalConfig object item: Mbox file or directory Returns: Collect mode to use """ if item.collectMode is None: collectMode = local.mbox.collectMode else: collectMode = item.collectMode logger.debug("Collect mode is [%s]", collectMode) return collectMode def _getCompressMode(local, item): """ Gets the compress mode that should be used for an mbox file or directory. Use file- or directory-specific value if possible, otherwise take from mbox section. Args: local: LocalConfig object item: Mbox file or directory Returns: Compress mode to use """ if item.compressMode is None: compressMode = local.mbox.compressMode else: compressMode = item.compressMode logger.debug("Compress mode is [%s]", compressMode) return compressMode def _getRevisionPath(config, item): """ Gets the path to the revision file associated with a repository. Args: config: Cedar Backup configuration item: Mbox file or directory Returns: Absolute path to the revision file associated with the repository """ normalized = buildNormalizedPath(item.absolutePath) filename = "%s.%s" % (normalized, REVISION_PATH_EXTENSION) revisionPath = pathJoin(config.options.workingDir, filename) logger.debug("Revision file path is [%s]", revisionPath) return revisionPath def _loadLastRevision(config, item, fullBackup, collectMode): """ Loads the last revision date for this item from disk and returns it. If this is a full backup, or if the revision file cannot be loaded for some reason, then ``None`` is returned. This indicates that there is no previous revision, so the entire mail file or directory should be backed up. *Note:* We write the actual revision object to disk via pickle, so we don't deal with the datetime precision or format at all. Whatever's in the object is what we write. Args: config: Cedar Backup configuration item: Mbox file or directory fullBackup: Indicates whether this is a full backup collectMode: Indicates the collect mode for this item Returns: Revision date as a datetime.datetime object or ``None`` """ revisionPath = _getRevisionPath(config, item) if fullBackup: revisionDate = None logger.debug("Revision file ignored because this is a full backup.") elif collectMode in ["weekly", "daily"]: revisionDate = None logger.debug("No revision file based on collect mode [%s].", collectMode) else: logger.debug("Revision file will be used for non-full incremental backup.") if not os.path.isfile(revisionPath): revisionDate = None logger.debug("Revision file [%s] does not exist on disk.", revisionPath) else: try: with open(revisionPath, "rb") as f: revisionDate = pickle.load(f, fix_imports=True) # be compatible with Python 2 logger.debug("Loaded revision file [%s] from disk: [%s]", revisionPath, revisionDate) except Exception as e: revisionDate = None logger.error("Failed loading revision file [%s] from disk: %s", revisionPath, e) return revisionDate def _writeNewRevision(config, item, newRevision): """ Writes new revision information to disk. If we can't write the revision file successfully for any reason, we'll log the condition but won't throw an exception. *Note:* We write the actual revision object to disk via pickle, so we don't deal with the datetime precision or format at all. Whatever's in the object is what we write. Args: config: Cedar Backup configuration item: Mbox file or directory newRevision: Revision date as a datetime.datetime object """ revisionPath = _getRevisionPath(config, item) try: with open(revisionPath, "wb") as f: pickle.dump(newRevision, f, 0, fix_imports=True) # be compatible with Python 2 changeOwnership(revisionPath, config.options.backupUser, config.options.backupGroup) logger.debug("Wrote new revision file [%s] to disk: [%s]", revisionPath, newRevision) except Exception as e: logger.error("Failed to write revision file [%s] to disk: %s", revisionPath, e) def _getExclusions(mboxDir): """ Gets exclusions (file and patterns) associated with an mbox directory. The returned files value is a list of absolute paths to be excluded from the backup for a given directory. It is derived from the mbox directory's relative exclude paths. The returned patterns value is a list of patterns to be excluded from the backup for a given directory. It is derived from the mbox directory's list of patterns. Args: mboxDir: Mbox directory object Returns: Tuple (files, patterns) indicating what to exclude """ paths = [] if mboxDir.relativeExcludePaths is not None: for relativePath in mboxDir.relativeExcludePaths: paths.append(pathJoin(mboxDir.absolutePath, relativePath)) patterns = [] if mboxDir.excludePatterns is not None: patterns.extend(mboxDir.excludePatterns) logger.debug("Exclude paths: %s", paths) logger.debug("Exclude patterns: %s", patterns) return (paths, patterns) def _getBackupPath(config, mboxPath, compressMode, newRevision, targetDir=None): """ Gets the backup file path (including correct extension) associated with an mbox path. We assume that if the target directory is passed in, that we're backing up a directory. Under these circumstances, we'll just use the basename of the individual path as the output file. *Note:* The backup path only contains the current date in YYYYMMDD format, but that's OK because the index information (stored elsewhere) is the actual date object. Args: config: Cedar Backup configuration mboxPath: Path to the indicated mbox file or directory compressMode: Compress mode to use for this mbox path newRevision: Revision this backup path represents targetDir: Target directory in which the path should exist Returns: Absolute path to the backup file associated with the repository """ if targetDir is None: normalizedPath = buildNormalizedPath(mboxPath) revisionDate = newRevision.strftime("%Y%m%d") filename = "mbox-%s-%s" % (revisionDate, normalizedPath) else: filename = os.path.basename(mboxPath) if compressMode == "gzip": filename = "%s.gz" % filename elif compressMode == "bzip2": filename = "%s.bz2" % filename if targetDir is None: backupPath = pathJoin(config.collect.targetDir, filename) else: backupPath = pathJoin(targetDir, filename) logger.debug("Backup file path is [%s]", backupPath) return backupPath def _getTarfilePath(config, mboxPath, compressMode, newRevision): """ Gets the tarfile backup file path (including correct extension) associated with an mbox path. Along with the path, the tar archive mode is returned in a form that can be used with :any:`BackupFileList.generateTarfile`. *Note:* The tarfile path only contains the current date in YYYYMMDD format, but that's OK because the index information (stored elsewhere) is the actual date object. Args: config: Cedar Backup configuration mboxPath: Path to the indicated mbox file or directory compressMode: Compress mode to use for this mbox path newRevision: Revision this backup path represents Returns: Tuple of (absolute path to tarfile, tar archive mode) """ normalizedPath = buildNormalizedPath(mboxPath) revisionDate = newRevision.strftime("%Y%m%d") filename = "mbox-%s-%s.tar" % (revisionDate, normalizedPath) if compressMode == "gzip": filename = "%s.gz" % filename archiveMode = "targz" elif compressMode == "bzip2": filename = "%s.bz2" % filename archiveMode = "tarbz2" else: archiveMode = "tar" tarfilePath = pathJoin(config.collect.targetDir, filename) logger.debug("Tarfile path is [%s]", tarfilePath) return (tarfilePath, archiveMode) def _getOutputFile(backupPath, compressMode): """ Opens the output file used for saving backup information. If the compress mode is "gzip", we'll open a ``GzipFile``, and if the compress mode is "bzip2", we'll open a ``BZ2File``. Otherwise, we'll just return an object from the normal ``open()`` method. Args: backupPath: Path to file to open compressMode: Compress mode of file ("none", "gzip", "bzip") Returns: Output file object, opened in binary mode for use with executeCommand() """ if compressMode == "gzip": return GzipFile(backupPath, "wb") elif compressMode == "bzip2": return BZ2File(backupPath, "wb") else: return open(backupPath, "wb") def _backupMboxFile(config, absolutePath, fullBackup, collectMode, compressMode, lastRevision, newRevision, targetDir=None): """ Backs up an individual mbox file. Args: config: Cedar Backup configuration absolutePath: Path to mbox file to back up fullBackup: Indicates whether this should be a full backup collectMode: Indicates the collect mode for this item compressMode: Compress mode of file ("none", "gzip", "bzip") lastRevision: Date of last backup as datetime.datetime newRevision: Date of new (current) backup as datetime.datetime targetDir: Target directory to write the backed-up file into Raises: ValueError: If some value is missing or invalid IOError: If there is a problem backing up the mbox file """ if fullBackup or collectMode != "incr" or lastRevision is None: args = ["-a", "-u", absolutePath] # remove duplicates but fetch entire mailbox else: revisionDate = lastRevision.strftime("%Y-%m-%dT%H:%M:%S") # ISO-8601 format; grepmail calls Date::Parse::str2time() args = ["-a", "-u", "-d", "since %s" % revisionDate, absolutePath] command = resolveCommand(GREPMAIL_COMMAND) backupPath = _getBackupPath(config, absolutePath, compressMode, newRevision, targetDir=targetDir) with _getOutputFile(backupPath, compressMode) as outputFile: result = executeCommand(command, args, returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=outputFile)[0] if result != 0: raise IOError("Error [%d] executing grepmail on [%s]." % (result, absolutePath)) logger.debug("Completed backing up mailbox [%s].", absolutePath) return backupPath def _backupMboxDir( config, absolutePath, fullBackup, collectMode, compressMode, lastRevision, newRevision, excludePaths, excludePatterns ): """ Backs up a directory containing mbox files. Args: config: Cedar Backup configuration absolutePath: Path to mbox directory to back up fullBackup: Indicates whether this should be a full backup collectMode: Indicates the collect mode for this item compressMode: Compress mode of file ("none", "gzip", "bzip") lastRevision: Date of last backup as datetime.datetime newRevision: Date of new (current) backup as datetime.datetime excludePaths: List of absolute paths to exclude excludePatterns: List of patterns to exclude Raises: ValueError: If some value is missing or invalid IOError: If there is a problem backing up the mbox file """ try: tmpdir = tempfile.mkdtemp(dir=config.options.workingDir) mboxList = FilesystemList() mboxList.excludeDirs = True mboxList.excludePaths = excludePaths mboxList.excludePatterns = excludePatterns mboxList.addDirContents(absolutePath, recursive=False) tarList = BackupFileList() for item in mboxList: backupPath = _backupMboxFile( config, item, fullBackup, collectMode, "none", # no need to compress inside compressed tar lastRevision, newRevision, targetDir=tmpdir, ) tarList.addFile(backupPath) (tarfilePath, archiveMode) = _getTarfilePath(config, absolutePath, compressMode, newRevision) tarList.generateTarfile(tarfilePath, archiveMode, ignore=True, flat=True) changeOwnership(tarfilePath, config.options.backupUser, config.options.backupGroup) logger.debug("Completed backing up directory [%s].", absolutePath) finally: try: for cleanitem in tarList: if os.path.exists(cleanitem): try: os.remove(cleanitem) except: pass except: pass try: os.rmdir(tmpdir) except: pass ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.144956 cedar_backup3-3.8.1/src/CedarBackup3/extend/mysql.py0000644000000000000000000006706414567004737017215 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2005,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides an extension to back up MySQL databases. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides an extension to back up MySQL databases. This is a Cedar Backup extension used to back up MySQL databases via the Cedar Backup command line. It requires a new configuration section and is intended to be run either immediately before or immediately after the standard collect action. Aside from its own configuration, it requires the options and collect configuration sections in the standard Cedar Backup configuration file. The backup is done via the ``mysqldump`` command included with the MySQL product. Output can be compressed using ``gzip`` or ``bzip2``. Administrators can configure the extension either to back up all databases or to back up only specific databases. Note that this code always produces a full backup. There is currently no facility for making incremental backups. If/when someone has a need for this and can describe how to do it, I'll update this extension or provide another. The extension assumes that all configured databases can be backed up by a single user. Often, the "root" database user will be used. An alternative is to create a separate MySQL "backup" user and grant that user rights to read (but not write) various databases as needed. This second option is probably the best choice. The extension accepts a username and password in configuration. However, you probably do not want to provide those values in Cedar Backup configuration. This is because Cedar Backup will provide these values to ``mysqldump`` via the command-line ``--user`` and ``--password`` switches, which will be visible to other users in the process listing. Instead, you should configure the username and password in one of MySQL's configuration files. Typically, that would be done by putting a stanza like this in ``/root/.my.cnf``:: [mysqldump] user = root password = Regardless of whether you are using ``~/.my.cnf`` or ``/etc/cback3.conf`` to store database login and password information, you should be careful about who is allowed to view that information. Typically, this means locking down permissions so that only the file owner can read the file contents (i.e. use mode ``0600``). :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os from bz2 import BZ2File from functools import total_ordering from gzip import GzipFile from CedarBackup3.config import VALID_COMPRESS_MODES from CedarBackup3.util import ObjectTypeList, changeOwnership, executeCommand, pathJoin, resolveCommand from CedarBackup3.xmlutil import ( addBooleanNode, addContainerNode, addStringNode, createInputDom, readBoolean, readFirstChild, readString, readStringList, ) ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.mysql") MYSQLDUMP_COMMAND = ["mysqldump"] ######################################################################## # MysqlConfig class definition ######################################################################## @total_ordering class MysqlConfig(object): """ Class representing MySQL configuration. The MySQL configuration information is used for backing up MySQL databases. The following restrictions exist on data in this class: - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`. - The 'all' flag must be 'Y' if no databases are defined. - The 'all' flag must be 'N' if any databases are defined. - Any values in the databases list must be strings. """ def __init__(self, user=None, password=None, compressMode=None, all=None, databases=None): # pylint: disable=W0622 """ Constructor for the ``MysqlConfig`` class. Args: user: User to execute backup as password: Password associated with user compressMode: Compress mode for backed-up files all: Indicates whether to back up all databases databases: List of databases to back up """ self._user = None self._password = None self._compressMode = None self._all = None self._databases = None self.user = user self.password = password self.compressMode = compressMode self.all = all self.databases = databases def __repr__(self): """ Official string representation for class instance. """ return "MysqlConfig(%s, %s, %s, %s)" % (self.user, self.password, self.all, self.databases) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.user != other.user: if str(self.user or "") < str(other.user or ""): return -1 else: return 1 if self.password != other.password: if str(self.password or "") < str(other.password or ""): return -1 else: return 1 if self.compressMode != other.compressMode: if str(self.compressMode or "") < str(other.compressMode or ""): return -1 else: return 1 if self.all != other.all: if self.all < other.all: return -1 else: return 1 if self.databases != other.databases: if self.databases < other.databases: return -1 else: return 1 return 0 def _setUser(self, value): """ Property target used to set the user value. """ if value is not None: if len(value) < 1: raise ValueError("User must be non-empty string.") self._user = value def _getUser(self): """ Property target used to get the user value. """ return self._user def _setPassword(self, value): """ Property target used to set the password value. """ if value is not None: if len(value) < 1: raise ValueError("Password must be non-empty string.") self._password = value def _getPassword(self): """ Property target used to get the password value. """ return self._password def _setCompressMode(self, value): """ Property target used to set the compress mode. If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COMPRESS_MODES: raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) self._compressMode = value def _getCompressMode(self): """ Property target used to get the compress mode. """ return self._compressMode def _setAll(self, value): """ Property target used to set the 'all' flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._all = True else: self._all = False def _getAll(self): """ Property target used to get the 'all' flag. """ return self._all def _setDatabases(self, value): """ Property target used to set the databases list. Either the value must be ``None`` or each element must be a string. Raises: ValueError: If the value is not a string """ if value is None: self._databases = None else: for database in value: if len(database) < 1: raise ValueError("Each database must be a non-empty string.") try: saved = self._databases self._databases = ObjectTypeList(str, "string") self._databases.extend(value) except Exception as e: self._databases = saved raise e def _getDatabases(self): """ Property target used to get the databases list. """ return self._databases user = property(_getUser, _setUser, None, "User to execute backup as.") password = property(_getPassword, _setPassword, None, "Password associated with user.") compressMode = property(_getCompressMode, _setCompressMode, None, "Compress mode to be used for backed-up files.") all = property(_getAll, _setAll, None, "Indicates whether to back up all databases.") databases = property(_getDatabases, _setDatabases, None, "List of databases to back up.") ######################################################################## # LocalConfig class definition ######################################################################## @total_ordering class LocalConfig(object): """ Class representing this extension's configuration document. This is not a general-purpose configuration object like the main Cedar Backup configuration object. Instead, it just knows how to parse and emit MySQL-specific configuration values. Third parties who need to read and write configuration related to this extension should access it through the constructor, ``validate`` and ``addConfig`` methods. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath`` then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._mysql = None self.mysql = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() def __repr__(self): """ Official string representation for class instance. """ return "LocalConfig(%s)" % (self.mysql) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.mysql != other.mysql: if self.mysql < other.mysql: return -1 else: return 1 return 0 def _setMysql(self, value): """ Property target used to set the mysql configuration value. If not ``None``, the value must be a ``MysqlConfig`` object. Raises: ValueError: If the value is not a ``MysqlConfig`` """ if value is None: self._mysql = None else: if not isinstance(value, MysqlConfig): raise ValueError("Value must be a ``MysqlConfig`` object.") self._mysql = value def _getMysql(self): """ Property target used to get the mysql configuration value. """ return self._mysql mysql = property(_getMysql, _setMysql, None, "Mysql configuration in terms of a ``MysqlConfig`` object.") def validate(self): """ Validates configuration represented by the object. The compress mode must be filled in. Then, if the 'all' flag *is* set, no databases are allowed, and if the 'all' flag is *not* set, at least one database is required. Raises: ValueError: If one of the validations fails """ if self.mysql is None: raise ValueError("Mysql section is required.") if self.mysql.compressMode is None: raise ValueError("Compress mode value is required.") if self.mysql.all: if self.mysql.databases is not None and self.mysql.databases != []: raise ValueError("Databases cannot be specified if 'all' flag is set.") else: if self.mysql.databases is None or len(self.mysql.databases) < 1: raise ValueError("At least one MySQL database must be indicated if 'all' flag is not set.") def addConfig(self, xmlDom, parentNode): """ Adds a configuration section as the next child of a parent. Third parties should use this function to write configuration related to this extension. We add the following fields to the document:: user //cb_config/mysql/user password //cb_config/mysql/password compressMode //cb_config/mysql/compress_mode all //cb_config/mysql/all We also add groups of the following items, one list element per item:: database //cb_config/mysql/database Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to """ if self.mysql is not None: sectionNode = addContainerNode(xmlDom, parentNode, "mysql") addStringNode(xmlDom, sectionNode, "user", self.mysql.user) addStringNode(xmlDom, sectionNode, "password", self.mysql.password) addStringNode(xmlDom, sectionNode, "compress_mode", self.mysql.compressMode) addBooleanNode(xmlDom, sectionNode, "all", self.mysql.all) if self.mysql.databases is not None: for database in self.mysql.databases: addStringNode(xmlDom, sectionNode, "database", database) def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls a static method to parse the mysql configuration section. Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._mysql = LocalConfig._parseMysql(parentNode) @staticmethod def _parseMysql(parentNode): """ Parses a mysql configuration section. We read the following fields:: user //cb_config/mysql/user password //cb_config/mysql/password compressMode //cb_config/mysql/compress_mode all //cb_config/mysql/all We also read groups of the following item, one list element per item:: databases //cb_config/mysql/database Args: parentNode: Parent node to search beneath Returns: ``MysqlConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ mysql = None section = readFirstChild(parentNode, "mysql") if section is not None: mysql = MysqlConfig() mysql.user = readString(section, "user") mysql.password = readString(section, "password") mysql.compressMode = readString(section, "compress_mode") mysql.all = readBoolean(section, "all") mysql.databases = readStringList(section, "database") return mysql ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### # pylint: disable=W0613 def executeAction(configPath, options, config): """ Executes the MySQL backup action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If a backup could not be written for some reason """ logger.debug("Executing MySQL extended action.") if config.options is None or config.collect is None: raise ValueError("Cedar Backup configuration is not properly filled in.") local = LocalConfig(xmlPath=configPath) if local.mysql.all: logger.info("Backing up all databases.") _backupDatabase( config.collect.targetDir, local.mysql.compressMode, local.mysql.user, local.mysql.password, config.options.backupUser, config.options.backupGroup, None, ) else: logger.debug("Backing up %d individual databases.", len(local.mysql.databases)) for database in local.mysql.databases: logger.info("Backing up database [%s].", database) _backupDatabase( config.collect.targetDir, local.mysql.compressMode, local.mysql.user, local.mysql.password, config.options.backupUser, config.options.backupGroup, database, ) logger.info("Executed the MySQL extended action successfully.") def _backupDatabase(targetDir, compressMode, user, password, backupUser, backupGroup, database=None): """ Backs up an individual MySQL database, or all databases. This internal method wraps the public method and adds some functionality, like figuring out a filename, etc. Args: targetDir: Directory into which backups should be written compressMode: Compress mode to be used for backed-up files user: User to use for connecting to the database (if any) password: Password associated with user (if any) backupUser: User to own resulting file backupGroup: Group to own resulting file database: Name of database, or ``None`` for all databases Returns: Name of the generated backup file Raises: ValueError: If some value is missing or invalid IOError: If there is a problem executing the MySQL dump """ (outputFile, filename) = _getOutputFile(targetDir, database, compressMode) with outputFile: backupDatabase(user, password, outputFile, database) if not os.path.exists(filename): raise IOError("Dump file [%s] does not seem to exist after backup completed." % filename) changeOwnership(filename, backupUser, backupGroup) def _getOutputFile(targetDir, database, compressMode): """ Opens the output file used for saving the MySQL dump. The filename is either ``"mysqldump.txt"`` or ``"mysqldump-.txt"``. The ``".bz2"`` extension is added if ``compress`` is ``True``. Args: targetDir: Target directory to write file in database: Name of the database (if any) compressMode: Compress mode to be used for backed-up files Returns: Tuple of (Output file object, filename), file opened in binary mode for use with executeCommand() """ if database is None: filename = pathJoin(targetDir, "mysqldump.txt") else: filename = pathJoin(targetDir, "mysqldump-%s.txt" % database) if compressMode == "gzip": filename = "%s.gz" % filename outputFile = GzipFile(filename, "wb") elif compressMode == "bzip2": filename = "%s.bz2" % filename outputFile = BZ2File(filename, "wb") else: outputFile = open(filename, "wb") logger.debug("MySQL dump file will be [%s].", filename) return (outputFile, filename) ############################ # backupDatabase() function ############################ def backupDatabase(user, password, backupFile, database=None): """ Backs up an individual MySQL database, or all databases. This function backs up either a named local MySQL database or all local MySQL databases, using the passed-in user and password (if provided) for connectivity. This function call *always* results a full backup. There is no facility for incremental backups. The backup data will be written into the passed-in backup file. Normally, this would be an object as returned from ``open``, but it is possible to use something like a ``GzipFile`` to write compressed output. The caller is responsible for closing the passed-in backup file. Often, the "root" database user will be used when backing up all databases. An alternative is to create a separate MySQL "backup" user and grant that user rights to read (but not write) all of the databases that will be backed up. This function accepts a username and password. However, you probably do not want to pass those values in. This is because they will be provided to ``mysqldump`` via the command-line ``--user`` and ``--password`` switches, which will be visible to other users in the process listing. Instead, you should configure the username and password in one of MySQL's configuration files. Typically, this would be done by putting a stanza like this in ``/root/.my.cnf``, to provide ``mysqldump`` with the root database username and its password:: [mysqldump] user = root password = If you are executing this function as some system user other than root, then the ``.my.cnf`` file would be placed in the home directory of that user. In either case, make sure to set restrictive permissions (typically, mode ``0600``) on ``.my.cnf`` to make sure that other users cannot read the file. Args: user (String representing MySQL username, or ``None``): User to use for connecting to the database (if any) password (String representing MySQL password, or ``None``): Password associated with user (if any) backupFile (Python file object as from ``open`` or ``file``): File use for writing backup database (String representing database name, or ``None`` for all databases): Name of the database to be backed up Raises: ValueError: If some value is missing or invalid IOError: If there is a problem executing the MySQL dump """ args = ["-all", "--flush-logs", "--opt"] if user is not None: logger.warning("Warning: MySQL username will be visible in process listing (consider using ~/.my.cnf).") args.append("--user=%s" % user) if password is not None: logger.warning("Warning: MySQL password will be visible in process listing (consider using ~/.my.cnf).") args.append("--password=%s" % password) if database is None: args.insert(0, "--all-databases") else: args.insert(0, "--databases") args.append(database) command = resolveCommand(MYSQLDUMP_COMMAND) result = executeCommand(command, args, returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=backupFile)[0] if result != 0: if database is None: raise IOError("Error [%d] executing MySQL database dump for all databases." % result) else: raise IOError("Error [%d] executing MySQL database dump for database [%s]." % (result, database)) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/extend/postgresql.py0000644000000000000000000006157714567004737020256 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2006,2010,2015 Kenneth J. Pronovici. # Copyright (c) 2006 Antoine Beaupre. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Antoine Beaupre # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides an extension to back up PostgreSQL databases. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # This file was created with a width of 132 characters, and NO tabs. ######################################################################## # Module documentation ######################################################################## """ Provides an extension to back up PostgreSQL databases. This is a Cedar Backup extension used to back up PostgreSQL databases via the Cedar Backup command line. It requires a new configurations section and is intended to be run either immediately before or immediately after the standard collect action. Aside from its own configuration, it requires the options and collect configuration sections in the standard Cedar Backup configuration file. The backup is done via the ``pg_dump`` or ``pg_dumpall`` commands included with the PostgreSQL product. Output can be compressed using ``gzip`` or ``bzip2``. Administrators can configure the extension either to back up all databases or to back up only specific databases. The extension assumes that the current user has passwordless access to the database since there is no easy way to pass a password to the ``pg_dump`` client. This can be accomplished using appropriate voodoo in the ``pg_hda.conf`` file. Note that this code always produces a full backup. There is currently no facility for making incremental backups. You should always make ``/etc/cback3.conf`` unreadble to non-root users once you place postgresql configuration into it, since postgresql configuration will contain information about available PostgreSQL databases and usernames. Use of this extension *may* expose usernames in the process listing (via ``ps``) when the backup is running if the username is specified in the configuration. :author: Kenneth J. Pronovici :author: Antoine Beaupre """ ######################################################################## # Imported modules ######################################################################## import logging import os from bz2 import BZ2File from functools import total_ordering from gzip import GzipFile from CedarBackup3.config import VALID_COMPRESS_MODES from CedarBackup3.util import ObjectTypeList, changeOwnership, executeCommand, pathJoin, resolveCommand from CedarBackup3.xmlutil import ( addBooleanNode, addContainerNode, addStringNode, createInputDom, readBoolean, readFirstChild, readString, readStringList, ) ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.postgresql") POSTGRESQLDUMP_COMMAND = ["pg_dump"] POSTGRESQLDUMPALL_COMMAND = ["pg_dumpall"] ######################################################################## # PostgresqlConfig class definition ######################################################################## @total_ordering class PostgresqlConfig(object): """ Class representing PostgreSQL configuration. The PostgreSQL configuration information is used for backing up PostgreSQL databases. The following restrictions exist on data in this class: - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`. - The 'all' flag must be 'Y' if no databases are defined. - The 'all' flag must be 'N' if any databases are defined. - Any values in the databases list must be strings. """ def __init__(self, user=None, compressMode=None, all=None, databases=None): # pylint: disable=W0622 """ Constructor for the ``PostgresqlConfig`` class. Args: user: User to execute backup as compressMode: Compress mode for backed-up files all: Indicates whether to back up all databases databases: List of databases to back up """ self._user = None self._compressMode = None self._all = None self._databases = None self.user = user self.compressMode = compressMode self.all = all self.databases = databases def __repr__(self): """ Official string representation for class instance. """ return "PostgresqlConfig(%s, %s, %s)" % (self.user, self.all, self.databases) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.user != other.user: if str(self.user or "") < str(other.user or ""): return -1 else: return 1 if self.compressMode != other.compressMode: if str(self.compressMode or "") < str(other.compressMode or ""): return -1 else: return 1 if self.all != other.all: if self.all < other.all: return -1 else: return 1 if self.databases != other.databases: if self.databases < other.databases: return -1 else: return 1 return 0 def _setUser(self, value): """ Property target used to set the user value. """ if value is not None: if len(value) < 1: raise ValueError("User must be non-empty string.") self._user = value def _getUser(self): """ Property target used to get the user value. """ return self._user def _setCompressMode(self, value): """ Property target used to set the compress mode. If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COMPRESS_MODES: raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) self._compressMode = value def _getCompressMode(self): """ Property target used to get the compress mode. """ return self._compressMode def _setAll(self, value): """ Property target used to set the 'all' flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._all = True else: self._all = False def _getAll(self): """ Property target used to get the 'all' flag. """ return self._all def _setDatabases(self, value): """ Property target used to set the databases list. Either the value must be ``None`` or each element must be a string. Raises: ValueError: If the value is not a string """ if value is None: self._databases = None else: for database in value: if len(database) < 1: raise ValueError("Each database must be a non-empty string.") try: saved = self._databases self._databases = ObjectTypeList(str, "string") self._databases.extend(value) except Exception as e: self._databases = saved raise e def _getDatabases(self): """ Property target used to get the databases list. """ return self._databases user = property(_getUser, _setUser, None, "User to execute backup as.") compressMode = property(_getCompressMode, _setCompressMode, None, "Compress mode to be used for backed-up files.") all = property(_getAll, _setAll, None, "Indicates whether to back up all databases.") databases = property(_getDatabases, _setDatabases, None, "List of databases to back up.") ######################################################################## # LocalConfig class definition ######################################################################## @total_ordering class LocalConfig(object): """ Class representing this extension's configuration document. This is not a general-purpose configuration object like the main Cedar Backup configuration object. Instead, it just knows how to parse and emit PostgreSQL-specific configuration values. Third parties who need to read and write configuration related to this extension should access it through the constructor, ``validate`` and ``addConfig`` methods. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath`` then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._postgresql = None self.postgresql = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() def __repr__(self): """ Official string representation for class instance. """ return "LocalConfig(%s)" % (self.postgresql) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.postgresql != other.postgresql: if self.postgresql < other.postgresql: return -1 else: return 1 return 0 def _setPostgresql(self, value): """ Property target used to set the postgresql configuration value. If not ``None``, the value must be a ``PostgresqlConfig`` object. Raises: ValueError: If the value is not a ``PostgresqlConfig`` """ if value is None: self._postgresql = None else: if not isinstance(value, PostgresqlConfig): raise ValueError("Value must be a ``PostgresqlConfig`` object.") self._postgresql = value def _getPostgresql(self): """ Property target used to get the postgresql configuration value. """ return self._postgresql postgresql = property( _getPostgresql, _setPostgresql, None, "Postgresql configuration in terms of a ``PostgresqlConfig`` object." ) def validate(self): """ Validates configuration represented by the object. The compress mode must be filled in. Then, if the 'all' flag *is* set, no databases are allowed, and if the 'all' flag is *not* set, at least one database is required. Raises: ValueError: If one of the validations fails """ if self.postgresql is None: raise ValueError("PostgreSQL section is required.") if self.postgresql.compressMode is None: raise ValueError("Compress mode value is required.") if self.postgresql.all: if self.postgresql.databases is not None and self.postgresql.databases != []: raise ValueError("Databases cannot be specified if 'all' flag is set.") else: if self.postgresql.databases is None or len(self.postgresql.databases) < 1: raise ValueError("At least one PostgreSQL database must be indicated if 'all' flag is not set.") def addConfig(self, xmlDom, parentNode): """ Adds a configuration section as the next child of a parent. Third parties should use this function to write configuration related to this extension. We add the following fields to the document:: user //cb_config/postgresql/user compressMode //cb_config/postgresql/compress_mode all //cb_config/postgresql/all We also add groups of the following items, one list element per item:: database //cb_config/postgresql/database Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to """ if self.postgresql is not None: sectionNode = addContainerNode(xmlDom, parentNode, "postgresql") addStringNode(xmlDom, sectionNode, "user", self.postgresql.user) addStringNode(xmlDom, sectionNode, "compress_mode", self.postgresql.compressMode) addBooleanNode(xmlDom, sectionNode, "all", self.postgresql.all) if self.postgresql.databases is not None: for database in self.postgresql.databases: addStringNode(xmlDom, sectionNode, "database", database) def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls a static method to parse the postgresql configuration section. Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._postgresql = LocalConfig._parsePostgresql(parentNode) @staticmethod def _parsePostgresql(parent): """ Parses a postgresql configuration section. We read the following fields:: user //cb_config/postgresql/user compressMode //cb_config/postgresql/compress_mode all //cb_config/postgresql/all We also read groups of the following item, one list element per item:: databases //cb_config/postgresql/database Args: parent: Parent node to search beneath Returns: ``PostgresqlConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ postgresql = None section = readFirstChild(parent, "postgresql") if section is not None: postgresql = PostgresqlConfig() postgresql.user = readString(section, "user") postgresql.compressMode = readString(section, "compress_mode") postgresql.all = readBoolean(section, "all") postgresql.databases = readStringList(section, "database") return postgresql ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### # pylint: disable=W0613 def executeAction(configPath, options, config): """ Executes the PostgreSQL backup action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If a backup could not be written for some reason """ logger.debug("Executing PostgreSQL extended action.") if config.options is None or config.collect is None: raise ValueError("Cedar Backup configuration is not properly filled in.") local = LocalConfig(xmlPath=configPath) if local.postgresql.all: logger.info("Backing up all databases.") _backupDatabase( config.collect.targetDir, local.postgresql.compressMode, local.postgresql.user, config.options.backupUser, config.options.backupGroup, None, ) if local.postgresql.databases is not None and local.postgresql.databases != []: logger.debug("Backing up %d individual databases.", len(local.postgresql.databases)) for database in local.postgresql.databases: logger.info("Backing up database [%s].", database) _backupDatabase( config.collect.targetDir, local.postgresql.compressMode, local.postgresql.user, config.options.backupUser, config.options.backupGroup, database, ) logger.info("Executed the PostgreSQL extended action successfully.") def _backupDatabase(targetDir, compressMode, user, backupUser, backupGroup, database=None): """ Backs up an individual PostgreSQL database, or all databases. This internal method wraps the public method and adds some functionality, like figuring out a filename, etc. Args: targetDir: Directory into which backups should be written compressMode: Compress mode to be used for backed-up files user: User to use for connecting to the database backupUser: User to own resulting file backupGroup: Group to own resulting file database: Name of database, or ``None`` for all databases Returns: Name of the generated backup file Raises: ValueError: If some value is missing or invalid IOError: If there is a problem executing the PostgreSQL dump """ (outputFile, filename) = _getOutputFile(targetDir, database, compressMode) with outputFile: backupDatabase(user, outputFile, database) if not os.path.exists(filename): raise IOError("Dump file [%s] does not seem to exist after backup completed." % filename) changeOwnership(filename, backupUser, backupGroup) def _getOutputFile(targetDir, database, compressMode): """ Opens the output file used for saving the PostgreSQL dump. The filename is either ``"postgresqldump.txt"`` or ``"postgresqldump-.txt"``. The ``".gz"`` or ``".bz2"`` extension is added if ``compress`` is ``True``. Args: targetDir: Target directory to write file in database: Name of the database (if any) compressMode: Compress mode to be used for backed-up files Returns: Tuple of (Output file object, filename), file opened in binary mode for use with executeCommand() """ if database is None: filename = pathJoin(targetDir, "postgresqldump.txt") else: filename = pathJoin(targetDir, "postgresqldump-%s.txt" % database) if compressMode == "gzip": filename = "%s.gz" % filename outputFile = GzipFile(filename, "wb") elif compressMode == "bzip2": filename = "%s.bz2" % filename outputFile = BZ2File(filename, "wb") else: outputFile = open(filename, "wb") logger.debug("PostgreSQL dump file will be [%s].", filename) return (outputFile, filename) ############################ # backupDatabase() function ############################ def backupDatabase(user, backupFile, database=None): """ Backs up an individual PostgreSQL database, or all databases. This function backs up either a named local PostgreSQL database or all local PostgreSQL databases, using the passed in user for connectivity. This is *always* a full backup. There is no facility for incremental backups. The backup data will be written into the passed-in back file. Normally, this would be an object as returned from ``open``, but it is possible to use something like a ``GzipFile`` to write compressed output. The caller is responsible for closing the passed-in backup file. *Note:* Typically, you would use the ``root`` user to back up all databases. Args: user (String representing PostgreSQL username): User to use for connecting to the database backupFile (Python file object as from ``open`` or ``file``): File use for writing backup database (String representing database name, or ``None`` for all databases): Name of the database to be backed up Raises: ValueError: If some value is missing or invalid IOError: If there is a problem executing the PostgreSQL dump """ args = [] if user is not None: args.append("-U") args.append(user) if database is None: command = resolveCommand(POSTGRESQLDUMPALL_COMMAND) else: command = resolveCommand(POSTGRESQLDUMP_COMMAND) args.append(database) result = executeCommand(command, args, returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=backupFile)[0] if result != 0: if database is None: raise IOError("Error [%d] executing PostgreSQL database dump for all databases." % result) else: raise IOError("Error [%d] executing PostgreSQL database dump for database [%s]." % (result, database)) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/extend/split.py0000644000000000000000000004717614567004737017205 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2013,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides an extension to split up large files in staging directories. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides an extension to split up large files in staging directories. When this extension is executed, it will look through the configured Cedar Backup staging directory for files exceeding a specified size limit, and split them down into smaller files using the 'split' utility. Any directory which has already been split (as indicated by the ``cback.split`` file) will be ignored. This extension requires a new configuration section and is intended to be run immediately after the standard stage action or immediately before the standard store action. Aside from its own configuration, it requires the options and staging configuration sections in the standard Cedar Backup configuration file. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import re from functools import total_ordering from CedarBackup3.actions.util import findDailyDirs, getBackupFiles, writeIndicatorFile from CedarBackup3.config import ByteQuantity, addByteQuantityNode, readByteQuantity from CedarBackup3.util import changeOwnership, executeCommand, resolveCommand from CedarBackup3.xmlutil import addContainerNode, createInputDom, readFirstChild ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.split") SPLIT_COMMAND = ["split"] SPLIT_INDICATOR = "cback.split" ######################################################################## # SplitConfig class definition ######################################################################## @total_ordering class SplitConfig(object): """ Class representing split configuration. Split configuration is used for splitting staging directories. The following restrictions exist on data in this class: - The size limit must be a ByteQuantity - The split size must be a ByteQuantity """ def __init__(self, sizeLimit=None, splitSize=None): """ Constructor for the ``SplitCOnfig`` class. Args: sizeLimit: Size limit of the files, in bytes splitSize: Size that files exceeding the limit will be split into, in bytes Raises: ValueError: If one of the values is invalid """ self._sizeLimit = None self._splitSize = None self.sizeLimit = sizeLimit self.splitSize = splitSize def __repr__(self): """ Official string representation for class instance. """ return "SplitConfig(%s, %s)" % (self.sizeLimit, self.splitSize) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.sizeLimit != other.sizeLimit: if (self.sizeLimit or ByteQuantity()) < (other.sizeLimit or ByteQuantity()): return -1 else: return 1 if self.splitSize != other.splitSize: if (self.splitSize or ByteQuantity()) < (other.splitSize or ByteQuantity()): return -1 else: return 1 return 0 def _setSizeLimit(self, value): """ Property target used to set the size limit. If not ``None``, the value must be a ``ByteQuantity`` object. Raises: ValueError: If the value is not a ``ByteQuantity`` """ if value is None: self._sizeLimit = None else: if not isinstance(value, ByteQuantity): raise ValueError("Value must be a ``ByteQuantity`` object.") self._sizeLimit = value def _getSizeLimit(self): """ Property target used to get the size limit. """ return self._sizeLimit def _setSplitSize(self, value): """ Property target used to set the split size. If not ``None``, the value must be a ``ByteQuantity`` object. Raises: ValueError: If the value is not a ``ByteQuantity`` """ if value is None: self._splitSize = None else: if not isinstance(value, ByteQuantity): raise ValueError("Value must be a ``ByteQuantity`` object.") self._splitSize = value def _getSplitSize(self): """ Property target used to get the split size. """ return self._splitSize sizeLimit = property(_getSizeLimit, _setSizeLimit, None, doc="Size limit, as a ByteQuantity") splitSize = property(_getSplitSize, _setSplitSize, None, doc="Split size, as a ByteQuantity") ######################################################################## # LocalConfig class definition ######################################################################## @total_ordering class LocalConfig(object): """ Class representing this extension's configuration document. This is not a general-purpose configuration object like the main Cedar Backup configuration object. Instead, it just knows how to parse and emit split-specific configuration values. Third parties who need to read and write configuration related to this extension should access it through the constructor, ``validate`` and ``addConfig`` methods. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath`` then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._split = None self.split = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() def __repr__(self): """ Official string representation for class instance. """ return "LocalConfig(%s)" % (self.split) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.split != other.split: if self.split < other.split: return -1 else: return 1 return 0 def _setSplit(self, value): """ Property target used to set the split configuration value. If not ``None``, the value must be a ``SplitConfig`` object. Raises: ValueError: If the value is not a ``SplitConfig`` """ if value is None: self._split = None else: if not isinstance(value, SplitConfig): raise ValueError("Value must be a ``SplitConfig`` object.") self._split = value def _getSplit(self): """ Property target used to get the split configuration value. """ return self._split split = property(_getSplit, _setSplit, None, "Split configuration in terms of a ``SplitConfig`` object.") def validate(self): """ Validates configuration represented by the object. Split configuration must be filled in. Within that, both the size limit and split size must be filled in. Raises: ValueError: If one of the validations fails """ if self.split is None: raise ValueError("Split section is required.") if self.split.sizeLimit is None: raise ValueError("Size limit must be set.") if self.split.splitSize is None: raise ValueError("Split size must be set.") def addConfig(self, xmlDom, parentNode): """ Adds a configuration section as the next child of a parent. Third parties should use this function to write configuration related to this extension. We add the following fields to the document:: sizeLimit //cb_config/split/size_limit splitSize //cb_config/split/split_size Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to """ if self.split is not None: sectionNode = addContainerNode(xmlDom, parentNode, "split") addByteQuantityNode(xmlDom, sectionNode, "size_limit", self.split.sizeLimit) addByteQuantityNode(xmlDom, sectionNode, "split_size", self.split.splitSize) def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls a static method to parse the split configuration section. Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._split = LocalConfig._parseSplit(parentNode) @staticmethod def _parseSplit(parent): """ Parses an split configuration section. We read the following individual fields:: sizeLimit //cb_config/split/size_limit splitSize //cb_config/split/split_size Args: parent: Parent node to search beneath Returns: ``EncryptConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ split = None section = readFirstChild(parent, "split") if section is not None: split = SplitConfig() split.sizeLimit = readByteQuantity(section, "size_limit") split.splitSize = readByteQuantity(section, "split_size") return split ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### # pylint: disable=W0613 def executeAction(configPath, options, config): """ Executes the split backup action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If there are I/O problems reading or writing files """ logger.debug("Executing split extended action.") if config.options is None or config.stage is None: raise ValueError("Cedar Backup configuration is not properly filled in.") local = LocalConfig(xmlPath=configPath) dailyDirs = findDailyDirs(config.stage.targetDir, SPLIT_INDICATOR) for dailyDir in dailyDirs: _splitDailyDir( dailyDir, local.split.sizeLimit, local.split.splitSize, config.options.backupUser, config.options.backupGroup ) writeIndicatorFile(dailyDir, SPLIT_INDICATOR, config.options.backupUser, config.options.backupGroup) logger.info("Executed the split extended action successfully.") ############################## # _splitDailyDir() function ############################## def _splitDailyDir(dailyDir, sizeLimit, splitSize, backupUser, backupGroup): """ Splits large files in a daily staging directory. Files that match INDICATOR_PATTERNS (i.e. ``"cback.store"``, ``"cback.stage"``, etc.) are assumed to be indicator files and are ignored. All other files are split. Args: dailyDir: Daily directory to encrypt sizeLimit: Size limit, in bytes splitSize: Split size, in bytes backupUser: User that target files should be owned by backupGroup: Group that target files should be owned by Raises: ValueError: If the encrypt mode is not supported ValueError: If the daily staging directory does not exist """ logger.debug("Begin splitting contents of [%s].", dailyDir) fileList = getBackupFiles(dailyDir) # ignores indicator files for path in fileList: size = float(os.stat(path).st_size) if size > sizeLimit: _splitFile(path, splitSize, backupUser, backupGroup, removeSource=True) logger.debug("Completed splitting contents of [%s].", dailyDir) ######################## # _splitFile() function ######################## def _splitFile(sourcePath, splitSize, backupUser, backupGroup, removeSource=False): """ Splits the source file into chunks of the indicated size. The split files will be owned by the indicated backup user and group. If ``removeSource`` is ``True``, then the source file will be removed after it is successfully split. Args: sourcePath: Absolute path of the source file to split splitSize: Encryption mode (only "gpg" is allowed) backupUser: User that target files should be owned by backupGroup: Group that target files should be owned by removeSource: Indicates whether to remove the source file Raises: IOError: If there is a problem accessing, splitting or removing the source file """ cwd = os.getcwd() try: if not os.path.exists(sourcePath): raise ValueError("Source path [%s] does not exist." % sourcePath) dirname = os.path.dirname(sourcePath) filename = os.path.basename(sourcePath) prefix = "%s_" % filename bytes = int(splitSize.bytes) # pylint: disable=W0622 os.chdir(dirname) # need to operate from directory that we want files written to command = resolveCommand(SPLIT_COMMAND) args = ["--verbose", "--numeric-suffixes", "--suffix-length=5", "--bytes=%d" % bytes, filename, prefix] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=False) if result != 0: raise IOError("Error [%d] calling split for [%s]." % (result, sourcePath)) pattern = re.compile(r"(creating file [`'])(%s)(.*)(')" % prefix) match = pattern.search(output[-1:][0]) if match is None: raise IOError("Unable to parse output from split command.") value = int(match.group(3).strip()) for index in range(0, value): path = "%s%05d" % (prefix, index) if not os.path.exists(path): raise IOError("After call to split, expected file [%s] does not exist." % path) changeOwnership(path, backupUser, backupGroup) if removeSource: if os.path.exists(sourcePath): try: os.remove(sourcePath) logger.debug("Completed removing old file [%s].", sourcePath) except: raise IOError("Failed to remove file [%s] after splitting it." % (sourcePath)) finally: os.chdir(cwd) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/extend/subversion.py0000644000000000000000000017327714567004737020253 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2005,2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides an extension to back up Subversion repositories. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides an extension to back up Subversion repositories. This is a Cedar Backup extension used to back up Subversion repositories via the Cedar Backup command line. Each Subversion repository can be backed using the same collect modes allowed for filesystems in the standard Cedar Backup collect action: weekly, daily, incremental. This extension requires a new configuration section and is intended to be run either immediately before or immediately after the standard collect action. Aside from its own configuration, it requires the options and collect configuration sections in the standard Cedar Backup configuration file. There are two different kinds of Subversion repositories at this writing: BDB (Berkeley Database) and FSFS (a "filesystem within a filesystem"). Although the repository type can be specified in configuration, that information is just kept around for reference. It doesn't affect the backup. Both kinds of repositories are backed up in the same way, using ``svnadmin dump`` in an incremental mode. It turns out that FSFS repositories can also be backed up just like any other filesystem directory. If you would rather do that, then use the normal collect action. This is probably simpler, although it carries its own advantages and disadvantages (plus you will have to be careful to exclude the working directories Subversion uses when building an update to commit). Check the Subversion documentation for more information. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import pickle from bz2 import BZ2File from functools import total_ordering from gzip import GzipFile from CedarBackup3.config import VALID_COLLECT_MODES, VALID_COMPRESS_MODES from CedarBackup3.filesystem import FilesystemList from CedarBackup3.util import ( ObjectTypeList, RegexList, UnorderedList, buildNormalizedPath, changeOwnership, encodePath, executeCommand, isStartOfWeek, pathJoin, resolveCommand, ) from CedarBackup3.xmlutil import ( addContainerNode, addStringNode, createInputDom, isElement, readChildren, readFirstChild, readString, readStringList, ) ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.subversion") SVNLOOK_COMMAND = ["svnlook"] SVNADMIN_COMMAND = ["svnadmin"] REVISION_PATH_EXTENSION = "svnlast" ######################################################################## # RepositoryDir class definition ######################################################################## @total_ordering class RepositoryDir(object): """ Class representing Subversion repository directory. A repository directory is a directory that contains one or more Subversion repositories. The following restrictions exist on data in this class: - The directory path must be absolute. - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`. The repository type value is kept around just for reference. It doesn't affect the behavior of the backup. Relative exclusions are allowed here. However, there is no configured ignore file, because repository dir backups are not recursive. """ def __init__( self, repositoryType=None, directoryPath=None, collectMode=None, compressMode=None, relativeExcludePaths=None, excludePatterns=None, ): """ Constructor for the ``RepositoryDir`` class. Args: repositoryType: Type of repository, for reference directoryPath: Absolute path of the Subversion parent directory collectMode: Overridden collect mode for this directory compressMode: Overridden compression mode for this directory relativeExcludePaths: List of relative paths to exclude excludePatterns: List of regular expression patterns to exclude """ self._repositoryType = None self._directoryPath = None self._collectMode = None self._compressMode = None self._relativeExcludePaths = None self._excludePatterns = None self.repositoryType = repositoryType self.directoryPath = directoryPath self.collectMode = collectMode self.compressMode = compressMode self.relativeExcludePaths = relativeExcludePaths self.excludePatterns = excludePatterns def __repr__(self): """ Official string representation for class instance. """ return "RepositoryDir(%s, %s, %s, %s, %s, %s)" % ( self.repositoryType, self.directoryPath, self.collectMode, self.compressMode, self.relativeExcludePaths, self.excludePatterns, ) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.repositoryType != other.repositoryType: if str(self.repositoryType or "") < str(other.repositoryType or ""): return -1 else: return 1 if self.directoryPath != other.directoryPath: if str(self.directoryPath or "") < str(other.directoryPath or ""): return -1 else: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.compressMode != other.compressMode: if str(self.compressMode or "") < str(other.compressMode or ""): return -1 else: return 1 if self.relativeExcludePaths != other.relativeExcludePaths: if self.relativeExcludePaths < other.relativeExcludePaths: return -1 else: return 1 if self.excludePatterns != other.excludePatterns: if self.excludePatterns < other.excludePatterns: return -1 else: return 1 return 0 def _setRepositoryType(self, value): """ Property target used to set the repository type. There is no validation; this value is kept around just for reference. """ self._repositoryType = value def _getRepositoryType(self): """ Property target used to get the repository type. """ return self._repositoryType def _setDirectoryPath(self, value): """ Property target used to set the directory path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Repository path must be an absolute path.") self._directoryPath = encodePath(value) def _getDirectoryPath(self): """ Property target used to get the repository path. """ return self._directoryPath def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of the values in :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setCompressMode(self, value): """ Property target used to set the compress mode. If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COMPRESS_MODES: raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) self._compressMode = value def _getCompressMode(self): """ Property target used to get the compress mode. """ return self._compressMode def _setRelativeExcludePaths(self, value): """ Property target used to set the relative exclude paths list. Elements do not have to exist on disk at the time of assignment. """ if value is None: self._relativeExcludePaths = None else: try: saved = self._relativeExcludePaths self._relativeExcludePaths = UnorderedList() self._relativeExcludePaths.extend(value) except Exception as e: self._relativeExcludePaths = saved raise e def _getRelativeExcludePaths(self): """ Property target used to get the relative exclude paths list. """ return self._relativeExcludePaths def _setExcludePatterns(self, value): """ Property target used to set the exclude patterns list. """ if value is None: self._excludePatterns = None else: try: saved = self._excludePatterns self._excludePatterns = RegexList() self._excludePatterns.extend(value) except Exception as e: self._excludePatterns = saved raise e def _getExcludePatterns(self): """ Property target used to get the exclude patterns list. """ return self._excludePatterns repositoryType = property(_getRepositoryType, _setRepositoryType, None, doc="Type of this repository, for reference.") directoryPath = property(_getDirectoryPath, _setDirectoryPath, None, doc="Absolute path of the Subversion parent directory.") collectMode = property(_getCollectMode, _setCollectMode, None, doc="Overridden collect mode for this repository.") compressMode = property(_getCompressMode, _setCompressMode, None, doc="Overridden compress mode for this repository.") relativeExcludePaths = property(_getRelativeExcludePaths, _setRelativeExcludePaths, None, "List of relative paths to exclude.") excludePatterns = property(_getExcludePatterns, _setExcludePatterns, None, "List of regular expression patterns to exclude.") ######################################################################## # Repository class definition ######################################################################## @total_ordering class Repository(object): """ Class representing generic Subversion repository configuration.. The following restrictions exist on data in this class: - The respository path must be absolute. - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`. The repository type value is kept around just for reference. It doesn't affect the behavior of the backup. """ def __init__(self, repositoryType=None, repositoryPath=None, collectMode=None, compressMode=None): """ Constructor for the ``Repository`` class. Args: repositoryType: Type of repository, for reference repositoryPath: Absolute path to a Subversion repository on disk collectMode: Overridden collect mode for this directory compressMode: Overridden compression mode for this directory """ self._repositoryType = None self._repositoryPath = None self._collectMode = None self._compressMode = None self.repositoryType = repositoryType self.repositoryPath = repositoryPath self.collectMode = collectMode self.compressMode = compressMode def __repr__(self): """ Official string representation for class instance. """ return "Repository(%s, %s, %s, %s)" % (self.repositoryType, self.repositoryPath, self.collectMode, self.compressMode) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.repositoryType != other.repositoryType: if str(self.repositoryType or "") < str(other.repositoryType or ""): return -1 else: return 1 if self.repositoryPath != other.repositoryPath: if str(self.repositoryPath or "") < str(other.repositoryPath or ""): return -1 else: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.compressMode != other.compressMode: if str(self.compressMode or "") < str(other.compressMode or ""): return -1 else: return 1 return 0 def _setRepositoryType(self, value): """ Property target used to set the repository type. There is no validation; this value is kept around just for reference. """ self._repositoryType = value def _getRepositoryType(self): """ Property target used to get the repository type. """ return self._repositoryType def _setRepositoryPath(self, value): """ Property target used to set the repository path. The value must be an absolute path if it is not ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Repository path must be an absolute path.") self._repositoryPath = encodePath(value) def _getRepositoryPath(self): """ Property target used to get the repository path. """ return self._repositoryPath def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of the values in :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setCompressMode(self, value): """ Property target used to set the compress mode. If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COMPRESS_MODES: raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) self._compressMode = value def _getCompressMode(self): """ Property target used to get the compress mode. """ return self._compressMode repositoryType = property(_getRepositoryType, _setRepositoryType, None, doc="Type of this repository, for reference.") repositoryPath = property(_getRepositoryPath, _setRepositoryPath, None, doc="Path to the repository to collect.") collectMode = property(_getCollectMode, _setCollectMode, None, doc="Overridden collect mode for this repository.") compressMode = property(_getCompressMode, _setCompressMode, None, doc="Overridden compress mode for this repository.") ######################################################################## # SubversionConfig class definition ######################################################################## @total_ordering class SubversionConfig(object): """ Class representing Subversion configuration. Subversion configuration is used for backing up Subversion repositories. The following restrictions exist on data in this class: - The collect mode must be one of the values in :any:`VALID_COLLECT_MODES`. - The compress mode must be one of the values in :any:`VALID_COMPRESS_MODES`. - The repositories list must be a list of ``Repository`` objects. - The repositoryDirs list must be a list of ``RepositoryDir`` objects. For the two lists, validation is accomplished through the :any:`util.ObjectTypeList` list implementation that overrides common list methods and transparently ensures that each element has the correct type. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, collectMode=None, compressMode=None, repositories=None, repositoryDirs=None): """ Constructor for the ``SubversionConfig`` class. Args: collectMode: Default collect mode compressMode: Default compress mode repositories: List of Subversion repositories to back up repositoryDirs: List of Subversion parent directories to back up Raises: ValueError: If one of the values is invalid """ self._collectMode = None self._compressMode = None self._repositories = None self._repositoryDirs = None self.collectMode = collectMode self.compressMode = compressMode self.repositories = repositories self.repositoryDirs = repositoryDirs def __repr__(self): """ Official string representation for class instance. """ return "SubversionConfig(%s, %s, %s, %s)" % (self.collectMode, self.compressMode, self.repositories, self.repositoryDirs) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.collectMode != other.collectMode: if str(self.collectMode or "") < str(other.collectMode or ""): return -1 else: return 1 if self.compressMode != other.compressMode: if str(self.compressMode or "") < str(other.compressMode or ""): return -1 else: return 1 if self.repositories != other.repositories: if self.repositories < other.repositories: return -1 else: return 1 if self.repositoryDirs != other.repositoryDirs: if self.repositoryDirs < other.repositoryDirs: return -1 else: return 1 return 0 def _setCollectMode(self, value): """ Property target used to set the collect mode. If not ``None``, the mode must be one of the values in :any:`VALID_COLLECT_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COLLECT_MODES: raise ValueError("Collect mode must be one of %s." % VALID_COLLECT_MODES) self._collectMode = value def _getCollectMode(self): """ Property target used to get the collect mode. """ return self._collectMode def _setCompressMode(self, value): """ Property target used to set the compress mode. If not ``None``, the mode must be one of the values in :any:`VALID_COMPRESS_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_COMPRESS_MODES: raise ValueError("Compress mode must be one of %s." % VALID_COMPRESS_MODES) self._compressMode = value def _getCompressMode(self): """ Property target used to get the compress mode. """ return self._compressMode def _setRepositories(self, value): """ Property target used to set the repositories list. Either the value must be ``None`` or each element must be a ``Repository``. Raises: ValueError: If the value is not a ``Repository`` """ if value is None: self._repositories = None else: try: saved = self._repositories self._repositories = ObjectTypeList(Repository, "Repository") self._repositories.extend(value) except Exception as e: self._repositories = saved raise e def _getRepositories(self): """ Property target used to get the repositories list. """ return self._repositories def _setRepositoryDirs(self, value): """ Property target used to set the repositoryDirs list. Either the value must be ``None`` or each element must be a ``Repository``. Raises: ValueError: If the value is not a ``Repository`` """ if value is None: self._repositoryDirs = None else: try: saved = self._repositoryDirs self._repositoryDirs = ObjectTypeList(RepositoryDir, "RepositoryDir") self._repositoryDirs.extend(value) except Exception as e: self._repositoryDirs = saved raise e def _getRepositoryDirs(self): """ Property target used to get the repositoryDirs list. """ return self._repositoryDirs collectMode = property(_getCollectMode, _setCollectMode, None, doc="Default collect mode.") compressMode = property(_getCompressMode, _setCompressMode, None, doc="Default compress mode.") repositories = property(_getRepositories, _setRepositories, None, doc="List of Subversion repositories to back up.") repositoryDirs = property(_getRepositoryDirs, _setRepositoryDirs, None, doc="List of Subversion parent directories to back up.") ######################################################################## # LocalConfig class definition ######################################################################## @total_ordering class LocalConfig(object): """ Class representing this extension's configuration document. This is not a general-purpose configuration object like the main Cedar Backup configuration object. Instead, it just knows how to parse and emit Subversion-specific configuration values. Third parties who need to read and write configuration related to this extension should access it through the constructor, ``validate`` and ``addConfig`` methods. *Note:* Lists within this class are "unordered" for equality comparisons. """ def __init__(self, xmlData=None, xmlPath=None, validate=True): """ Initializes a configuration object. If you initialize the object without passing either ``xmlData`` or ``xmlPath`` then configuration will be empty and will be invalid until it is filled in properly. No reference to the original XML data or original path is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. Unless the ``validate`` argument is ``False``, the :any:`LocalConfig.validate` method will be called (with its default arguments) against configuration after successfully parsing any passed-in XML. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in XML document if lower-level validations fail. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid configuration from disk. Args: xmlData (String data): XML data representing configuration xmlPath (Absolute path to a file on disk): Path to an XML file on disk validate (Boolean true/false): Validate the document after parsing it Raises: ValueError: If both ``xmlData`` and ``xmlPath`` are passed-in ValueError: If the XML data in ``xmlData`` or ``xmlPath`` cannot be parsed ValueError: If the parsed configuration document is not valid """ self._subversion = None self.subversion = None if xmlData is not None and xmlPath is not None: raise ValueError("Use either xmlData or xmlPath, but not both.") if xmlData is not None: self._parseXmlData(xmlData) if validate: self.validate() elif xmlPath is not None: with open(xmlPath) as f: # pylint: disable=unspecified-encoding xmlData = f.read() self._parseXmlData(xmlData) if validate: self.validate() def __repr__(self): """ Official string representation for class instance. """ return "LocalConfig(%s)" % (self.subversion) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.subversion != other.subversion: if self.subversion < other.subversion: return -1 else: return 1 return 0 def _setSubversion(self, value): """ Property target used to set the subversion configuration value. If not ``None``, the value must be a ``SubversionConfig`` object. Raises: ValueError: If the value is not a ``SubversionConfig`` """ if value is None: self._subversion = None else: if not isinstance(value, SubversionConfig): raise ValueError("Value must be a ``SubversionConfig`` object.") self._subversion = value def _getSubversion(self): """ Property target used to get the subversion configuration value. """ return self._subversion subversion = property( _getSubversion, _setSubversion, None, "Subversion configuration in terms of a ``SubversionConfig`` object." ) def validate(self): """ Validates configuration represented by the object. Subversion configuration must be filled in. Within that, the collect mode and compress mode are both optional, but the list of repositories must contain at least one entry. Each repository must contain a repository path, and then must be either able to take collect mode and compress mode configuration from the parent ``SubversionConfig`` object, or must set each value on its own. Raises: ValueError: If one of the validations fails """ if self.subversion is None: raise ValueError("Subversion section is required.") if (self.subversion.repositories is None or len(self.subversion.repositories) < 1) and ( self.subversion.repositoryDirs is None or len(self.subversion.repositoryDirs) < 1 ): raise ValueError("At least one Subversion repository must be configured.") if self.subversion.repositories is not None: for repository in self.subversion.repositories: if repository.repositoryPath is None: raise ValueError("Each repository must set a repository path.") if self.subversion.collectMode is None and repository.collectMode is None: raise ValueError("Collect mode must either be set in parent section or individual repository.") if self.subversion.compressMode is None and repository.compressMode is None: raise ValueError("Compress mode must either be set in parent section or individual repository.") if self.subversion.repositoryDirs is not None: for repositoryDir in self.subversion.repositoryDirs: if repositoryDir.directoryPath is None: raise ValueError("Each repository directory must set a directory path.") if self.subversion.collectMode is None and repositoryDir.collectMode is None: raise ValueError("Collect mode must either be set in parent section or repository directory.") if self.subversion.compressMode is None and repositoryDir.compressMode is None: raise ValueError("Compress mode must either be set in parent section or repository directory.") def addConfig(self, xmlDom, parentNode): """ Adds a configuration section as the next child of a parent. Third parties should use this function to write configuration related to this extension. We add the following fields to the document:: collectMode //cb_config/subversion/collectMode compressMode //cb_config/subversion/compressMode We also add groups of the following items, one list element per item:: repository //cb_config/subversion/repository repository_dir //cb_config/subversion/repository_dir Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to """ if self.subversion is not None: sectionNode = addContainerNode(xmlDom, parentNode, "subversion") addStringNode(xmlDom, sectionNode, "collect_mode", self.subversion.collectMode) addStringNode(xmlDom, sectionNode, "compress_mode", self.subversion.compressMode) if self.subversion.repositories is not None: for repository in self.subversion.repositories: LocalConfig._addRepository(xmlDom, sectionNode, repository) if self.subversion.repositoryDirs is not None: for repositoryDir in self.subversion.repositoryDirs: LocalConfig._addRepositoryDir(xmlDom, sectionNode, repositoryDir) def _parseXmlData(self, xmlData): """ Internal method to parse an XML string into the object. This method parses the XML document into a DOM tree (``xmlDom``) and then calls a static method to parse the subversion configuration section. Args: xmlData (String data): XML data to be parsed Raises: ValueError: If the XML cannot be successfully parsed """ (xmlDom, parentNode) = createInputDom(xmlData) self._subversion = LocalConfig._parseSubversion(parentNode) @staticmethod def _parseSubversion(parent): """ Parses a subversion configuration section. We read the following individual fields:: collectMode //cb_config/subversion/collect_mode compressMode //cb_config/subversion/compress_mode We also read groups of the following item, one list element per item:: repositories //cb_config/subversion/repository repository_dirs //cb_config/subversion/repository_dir The repositories are parsed by :any:`_parseRepositories`, and the repository dirs are parsed by :any:`_parseRepositoryDirs`. Args: parent: Parent node to search beneath Returns: ``SubversionConfig`` object or ``None`` if the section does not exist Raises: ValueError: If some filled-in value is invalid """ subversion = None section = readFirstChild(parent, "subversion") if section is not None: subversion = SubversionConfig() subversion.collectMode = readString(section, "collect_mode") subversion.compressMode = readString(section, "compress_mode") subversion.repositories = LocalConfig._parseRepositories(section) subversion.repositoryDirs = LocalConfig._parseRepositoryDirs(section) return subversion @staticmethod def _parseRepositories(parent): """ Reads a list of ``Repository`` objects from immediately beneath the parent. We read the following individual fields:: repositoryType type repositoryPath abs_path collectMode collect_mode compressMode compess_mode The type field is optional, and its value is kept around only for reference. Args: parent: Parent node to search beneath Returns: List of ``Repository`` objects or ``None`` if none are found Raises: ValueError: If some filled-in value is invalid """ lst = [] for entry in readChildren(parent, "repository"): if isElement(entry): repository = Repository() repository.repositoryType = readString(entry, "type") repository.repositoryPath = readString(entry, "abs_path") repository.collectMode = readString(entry, "collect_mode") repository.compressMode = readString(entry, "compress_mode") lst.append(repository) if not lst: lst = None return lst @staticmethod def _addRepository(xmlDom, parentNode, repository): """ Adds a repository container as the next child of a parent. We add the following fields to the document:: repositoryType repository/type repositoryPath repository/abs_path collectMode repository/collect_mode compressMode repository/compress_mode The node itself is created as the next child of the parent node. This method only adds one repository node. The parent must loop for each repository in the ``SubversionConfig`` object. If ``repository`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to repository: Repository to be added to the document """ if repository is not None: sectionNode = addContainerNode(xmlDom, parentNode, "repository") addStringNode(xmlDom, sectionNode, "type", repository.repositoryType) addStringNode(xmlDom, sectionNode, "abs_path", repository.repositoryPath) addStringNode(xmlDom, sectionNode, "collect_mode", repository.collectMode) addStringNode(xmlDom, sectionNode, "compress_mode", repository.compressMode) @staticmethod def _parseRepositoryDirs(parent): """ Reads a list of ``RepositoryDir`` objects from immediately beneath the parent. We read the following individual fields:: repositoryType type directoryPath abs_path collectMode collect_mode compressMode compess_mode We also read groups of the following items, one list element per item:: relativeExcludePaths exclude/rel_path excludePatterns exclude/pattern The exclusions are parsed by :any:`_parseExclusions`. The type field is optional, and its value is kept around only for reference. Args: parent: Parent node to search beneath Returns: List of ``RepositoryDir`` objects or ``None`` if none are found Raises: ValueError: If some filled-in value is invalid """ lst = [] for entry in readChildren(parent, "repository_dir"): if isElement(entry): repositoryDir = RepositoryDir() repositoryDir.repositoryType = readString(entry, "type") repositoryDir.directoryPath = readString(entry, "abs_path") repositoryDir.collectMode = readString(entry, "collect_mode") repositoryDir.compressMode = readString(entry, "compress_mode") (repositoryDir.relativeExcludePaths, repositoryDir.excludePatterns) = LocalConfig._parseExclusions(entry) lst.append(repositoryDir) if not lst: lst = None return lst @staticmethod def _parseExclusions(parentNode): """ Reads exclusions data from immediately beneath the parent. We read groups of the following items, one list element per item:: relative exclude/rel_path patterns exclude/pattern If there are none of some pattern (i.e. no relative path items) then ``None`` will be returned for that item in the tuple. Args: parentNode: Parent node to search beneath Returns: Tuple of (relative, patterns) exclusions """ section = readFirstChild(parentNode, "exclude") if section is None: return (None, None) else: relative = readStringList(section, "rel_path") patterns = readStringList(section, "pattern") return (relative, patterns) @staticmethod def _addRepositoryDir(xmlDom, parentNode, repositoryDir): """ Adds a repository dir container as the next child of a parent. We add the following fields to the document:: repositoryType repository_dir/type directoryPath repository_dir/abs_path collectMode repository_dir/collect_mode compressMode repository_dir/compress_mode We also add groups of the following items, one list element per item:: relativeExcludePaths dir/exclude/rel_path excludePatterns dir/exclude/pattern The node itself is created as the next child of the parent node. This method only adds one repository node. The parent must loop for each repository dir in the ``SubversionConfig`` object. If ``repositoryDir`` is ``None``, this method call will be a no-op. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent that the section should be appended to repositoryDir: Repository dir to be added to the document """ if repositoryDir is not None: sectionNode = addContainerNode(xmlDom, parentNode, "repository_dir") addStringNode(xmlDom, sectionNode, "type", repositoryDir.repositoryType) addStringNode(xmlDom, sectionNode, "abs_path", repositoryDir.directoryPath) addStringNode(xmlDom, sectionNode, "collect_mode", repositoryDir.collectMode) addStringNode(xmlDom, sectionNode, "compress_mode", repositoryDir.compressMode) if (repositoryDir.relativeExcludePaths is not None and repositoryDir.relativeExcludePaths != []) or ( repositoryDir.excludePatterns is not None and repositoryDir.excludePatterns != [] ): excludeNode = addContainerNode(xmlDom, sectionNode, "exclude") if repositoryDir.relativeExcludePaths is not None: for relativePath in repositoryDir.relativeExcludePaths: addStringNode(xmlDom, excludeNode, "rel_path", relativePath) if repositoryDir.excludePatterns is not None: for pattern in repositoryDir.excludePatterns: addStringNode(xmlDom, excludeNode, "pattern", pattern) ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### def executeAction(configPath, options, config): """ Executes the Subversion backup action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If a backup could not be written for some reason """ logger.debug("Executing Subversion extended action.") if config.options is None or config.collect is None: raise ValueError("Cedar Backup configuration is not properly filled in.") local = LocalConfig(xmlPath=configPath) todayIsStart = isStartOfWeek(config.options.startingDay) fullBackup = options.full or todayIsStart logger.debug("Full backup flag is [%s]", fullBackup) if local.subversion.repositories is not None: for repository in local.subversion.repositories: _backupRepository(config, local, todayIsStart, fullBackup, repository) if local.subversion.repositoryDirs is not None: for repositoryDir in local.subversion.repositoryDirs: logger.debug("Working with repository directory [%s].", repositoryDir.directoryPath) for repositoryPath in _getRepositoryPaths(repositoryDir): repository = Repository( repositoryDir.repositoryType, repositoryPath, repositoryDir.collectMode, repositoryDir.compressMode ) _backupRepository(config, local, todayIsStart, fullBackup, repository) logger.info("Completed backing up Subversion repository directory [%s].", repositoryDir.directoryPath) logger.info("Executed the Subversion extended action successfully.") def _getCollectMode(local, repository): """ Gets the collect mode that should be used for a repository. Use repository's if possible, otherwise take from subversion section. Args: repository: Repository object Returns: Collect mode to use """ if repository.collectMode is None: collectMode = local.subversion.collectMode else: collectMode = repository.collectMode logger.debug("Collect mode is [%s]", collectMode) return collectMode def _getCompressMode(local, repository): """ Gets the compress mode that should be used for a repository. Use repository's if possible, otherwise take from subversion section. Args: local: LocalConfig object repository: Repository object Returns: Compress mode to use """ if repository.compressMode is None: compressMode = local.subversion.compressMode else: compressMode = repository.compressMode logger.debug("Compress mode is [%s]", compressMode) return compressMode def _getRevisionPath(config, repository): """ Gets the path to the revision file associated with a repository. Args: config: Config object repository: Repository object Returns: Absolute path to the revision file associated with the repository """ normalized = buildNormalizedPath(repository.repositoryPath) filename = "%s.%s" % (normalized, REVISION_PATH_EXTENSION) revisionPath = pathJoin(config.options.workingDir, filename) logger.debug("Revision file path is [%s]", revisionPath) return revisionPath def _getBackupPath(config, repositoryPath, compressMode, startRevision, endRevision): """ Gets the backup file path (including correct extension) associated with a repository. Args: config: Config object repositoryPath: Path to the indicated repository compressMode: Compress mode to use for this repository startRevision: Starting repository revision endRevision: Ending repository revision Returns: Absolute path to the backup file associated with the repository """ normalizedPath = buildNormalizedPath(repositoryPath) filename = "svndump-%d:%d-%s.txt" % (startRevision, endRevision, normalizedPath) if compressMode == "gzip": filename = "%s.gz" % filename elif compressMode == "bzip2": filename = "%s.bz2" % filename backupPath = pathJoin(config.collect.targetDir, filename) logger.debug("Backup file path is [%s]", backupPath) return backupPath def _getRepositoryPaths(repositoryDir): """ Gets a list of child repository paths within a repository directory. Args: repositoryDir: RepositoryDirectory """ (excludePaths, excludePatterns) = _getExclusions(repositoryDir) fsList = FilesystemList() fsList.excludeFiles = True fsList.excludeLinks = True fsList.excludePaths = excludePaths fsList.excludePatterns = excludePatterns fsList.addDirContents(path=repositoryDir.directoryPath, recursive=False, addSelf=False) return fsList def _getExclusions(repositoryDir): """ Gets exclusions (file and patterns) associated with an repository directory. The returned files value is a list of absolute paths to be excluded from the backup for a given directory. It is derived from the repository directory's relative exclude paths. The returned patterns value is a list of patterns to be excluded from the backup for a given directory. It is derived from the repository directory's list of patterns. Args: repositoryDir: Repository directory object Returns: Tuple (files, patterns) indicating what to exclude """ paths = [] if repositoryDir.relativeExcludePaths is not None: for relativePath in repositoryDir.relativeExcludePaths: paths.append(pathJoin(repositoryDir.directoryPath, relativePath)) patterns = [] if repositoryDir.excludePatterns is not None: patterns.extend(repositoryDir.excludePatterns) logger.debug("Exclude paths: %s", paths) logger.debug("Exclude patterns: %s", patterns) return (paths, patterns) def _backupRepository(config, local, todayIsStart, fullBackup, repository): """ Backs up an individual Subversion repository. This internal method wraps the public methods and adds some functionality to work better with the extended action itself. Args: config: Cedar Backup configuration local: Local configuration todayIsStart: Indicates whether today is start of week fullBackup: Full backup flag repository: Repository to operate on Raises: ValueError: If some value is missing or invalid IOError: If there is a problem executing the Subversion dump """ logger.debug("Working with repository [%s]", repository.repositoryPath) logger.debug("Repository type is [%s]", repository.repositoryType) collectMode = _getCollectMode(local, repository) compressMode = _getCompressMode(local, repository) revisionPath = _getRevisionPath(config, repository) if not (fullBackup or (collectMode in ["daily", "incr"]) or (collectMode == "weekly" and todayIsStart)): logger.debug("Repository will not be backed up, per collect mode.") return logger.debug("Repository meets criteria to be backed up today.") if collectMode != "incr" or fullBackup: startRevision = 0 endRevision = getYoungestRevision(repository.repositoryPath) logger.debug("Using full backup, revision: (%d, %d).", startRevision, endRevision) else: if fullBackup: startRevision = 0 endRevision = getYoungestRevision(repository.repositoryPath) else: startRevision = _loadLastRevision(revisionPath) + 1 endRevision = getYoungestRevision(repository.repositoryPath) if startRevision > endRevision: logger.info("No need to back up repository [%s]; no new revisions.", repository.repositoryPath) return logger.debug("Using incremental backup, revision: (%d, %d).", startRevision, endRevision) backupPath = _getBackupPath(config, repository.repositoryPath, compressMode, startRevision, endRevision) with _getOutputFile(backupPath, compressMode) as outputFile: backupRepository(repository.repositoryPath, outputFile, startRevision, endRevision) if not os.path.exists(backupPath): raise IOError("Dump file [%s] does not seem to exist after backup completed." % backupPath) changeOwnership(backupPath, config.options.backupUser, config.options.backupGroup) if collectMode == "incr": _writeLastRevision(config, revisionPath, endRevision) logger.info("Completed backing up Subversion repository [%s].", repository.repositoryPath) def _getOutputFile(backupPath, compressMode): """ Opens the output file used for saving the Subversion dump. If the compress mode is "gzip", we'll open a ``GzipFile``, and if the compress mode is "bzip2", we'll open a ``BZ2File``. Otherwise, we'll just return an object from the normal ``open()`` method. Args: backupPath: Path to file to open compressMode: Compress mode of file ("none", "gzip", "bzip") Returns: Output file object, opened in binary mode for use with executeCommand() """ if compressMode == "gzip": return GzipFile(backupPath, "wb") elif compressMode == "bzip2": return BZ2File(backupPath, "wb") else: return open(backupPath, "wb") def _loadLastRevision(revisionPath): """ Loads the indicated revision file from disk into an integer. If we can't load the revision file successfully (either because it doesn't exist or for some other reason), then a revision of -1 will be returned - but the condition will be logged. This way, we err on the side of backing up too much, because anyone using this will presumably be adding 1 to the revision, so they don't duplicate any backups. Args: revisionPath: Path to the revision file on disk Returns: Integer representing last backed-up revision, -1 on error or if none can be read """ if not os.path.isfile(revisionPath): startRevision = -1 logger.debug("Revision file [%s] does not exist on disk.", revisionPath) else: try: with open(revisionPath, "rb") as f: startRevision = pickle.load(f, fix_imports=True) # be compatible with Python 2 logger.debug("Loaded revision file [%s] from disk: %d.", revisionPath, startRevision) except Exception as e: startRevision = -1 logger.error("Failed loading revision file [%s] from disk: %s", revisionPath, e) return startRevision def _writeLastRevision(config, revisionPath, endRevision): """ Writes the end revision to the indicated revision file on disk. If we can't write the revision file successfully for any reason, we'll log the condition but won't throw an exception. Args: config: Config object revisionPath: Path to the revision file on disk endRevision: Last revision backed up on this run """ try: with open(revisionPath, "wb") as f: pickle.dump(endRevision, f, 0, fix_imports=True) changeOwnership(revisionPath, config.options.backupUser, config.options.backupGroup) logger.debug("Wrote new revision file [%s] to disk: %d.", revisionPath, endRevision) except Exception as e: logger.error("Failed to write revision file [%s] to disk: %s", revisionPath, e) ############################## # backupRepository() function ############################## def backupRepository(repositoryPath, backupFile, startRevision=None, endRevision=None): """ Backs up an individual Subversion repository. The starting and ending revision values control an incremental backup. If the starting revision is not passed in, then revision zero (the start of the repository) is assumed. If the ending revision is not passed in, then the youngest revision in the database will be used as the endpoint. The backup data will be written into the passed-in back file. Normally, this would be an object as returned from ``open``, but it is possible to use something like a ``GzipFile`` to write compressed output. The caller is responsible for closing the passed-in backup file. *Note:* This function should either be run as root or as the owner of the Subversion repository. *Note:* It is apparently *not* a good idea to interrupt this function. Sometimes, this leaves the repository in a "wedged" state, which requires recovery using ``svnadmin recover``. Args: repositoryPath (String path representing Subversion repository on disk): Path to Subversion repository to back up backupFile (Python file object as from ``open`` or ``file``): Python file object to use for writing backup startRevision (Integer value >= 0): Starting repository revision to back up (for incremental backups) endRevision (Integer value >= 0): Ending repository revision to back up (for incremental backups) Raises: ValueError: If some value is missing or invalid IOError: If there is a problem executing the Subversion dump """ if startRevision is None: startRevision = 0 if endRevision is None: endRevision = getYoungestRevision(repositoryPath) if int(startRevision) < 0: raise ValueError("Start revision must be >= 0.") if int(endRevision) < 0: raise ValueError("End revision must be >= 0.") if startRevision > endRevision: raise ValueError("Start revision must be <= end revision.") args = ["dump", "--quiet", "-r%s:%s" % (startRevision, endRevision), "--incremental", repositoryPath] command = resolveCommand(SVNADMIN_COMMAND) result = executeCommand(command, args, returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=backupFile)[0] if result != 0: raise IOError("Error [%d] executing Subversion dump for repository [%s]." % (result, repositoryPath)) logger.debug("Completed dumping subversion repository [%s].", repositoryPath) ################################# # getYoungestRevision() function ################################# def getYoungestRevision(repositoryPath): """ Gets the youngest (newest) revision in a Subversion repository using ``svnlook``. *Note:* This function should either be run as root or as the owner of the Subversion repository. Args: repositoryPath (String path representing Subversion repository on disk): Path to Subversion repository to look in Returns: Youngest revision as an integer Raises: ValueError: If there is a problem parsing the ``svnlook`` output IOError: If there is a problem executing the ``svnlook`` command """ args = ["youngest", repositoryPath] command = resolveCommand(SVNLOOK_COMMAND) (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) if result != 0: raise IOError("Error [%d] executing 'svnlook youngest' for repository [%s]." % (result, repositoryPath)) if len(output) != 1: raise ValueError("Unable to parse 'svnlook youngest' output.") return int(output[0]) ######################################################################## # Deprecated functionality ######################################################################## class BDBRepository(Repository): """ Class representing Subversion BDB (Berkeley Database) repository configuration. This object is deprecated. Use a simple :any:`Repository` instead. """ def __init__(self, repositoryPath=None, collectMode=None, compressMode=None): """ Constructor for the ``BDBRepository`` class. """ super(BDBRepository, self).__init__("BDB", repositoryPath, collectMode, compressMode) def __repr__(self): """ Official string representation for class instance. """ return "BDBRepository(%s, %s, %s)" % (self.repositoryPath, self.collectMode, self.compressMode) class FSFSRepository(Repository): """ Class representing Subversion FSFS repository configuration. This object is deprecated. Use a simple :any:`Repository` instead. """ def __init__(self, repositoryPath=None, collectMode=None, compressMode=None): """ Constructor for the ``FSFSRepository`` class. """ super(FSFSRepository, self).__init__("FSFS", repositoryPath, collectMode, compressMode) def __repr__(self): """ Official string representation for class instance. """ return "FSFSRepository(%s, %s, %s)" % (self.repositoryPath, self.collectMode, self.compressMode) def backupBDBRepository(repositoryPath, backupFile, startRevision=None, endRevision=None): """ Backs up an individual Subversion BDB repository. This function is deprecated. Use :any:`backupRepository` instead. """ return backupRepository(repositoryPath, backupFile, startRevision, endRevision) def backupFSFSRepository(repositoryPath, backupFile, startRevision=None, endRevision=None): """ Backs up an individual Subversion FSFS repository. This function is deprecated. Use :any:`backupRepository` instead. """ return backupRepository(repositoryPath, backupFile, startRevision, endRevision) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/extend/sysinfo.py0000644000000000000000000002156314567004737017534 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2005,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides an extension to save off important system recovery information. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides an extension to save off important system recovery information. This is a simple Cedar Backup extension used to save off important system recovery information. It saves off three types of information: - Currently-installed Debian packages via ``dpkg --get-selections`` - Disk partition information via ``fdisk -l`` - System-wide mounted filesystem contents, via ``ls -laR`` The saved-off information is placed into the collect directory and is compressed using ``bzip2`` to save space. This extension relies on the options and collect configurations in the standard Cedar Backup configuration file, but requires no new configuration of its own. No public functions other than the action are exposed since all of this is pretty simple. *Note:* If the ``dpkg`` or ``fdisk`` commands cannot be found in their normal locations or executed by the current user, those steps will be skipped and a note will be logged at the INFO level. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os from bz2 import BZ2File from CedarBackup3.util import changeOwnership, executeCommand, pathJoin, resolveCommand ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.extend.sysinfo") DPKG_PATH = "/usr/bin/dpkg" FDISK_PATH = "/sbin/fdisk" DPKG_COMMAND = [DPKG_PATH, "--get-selections"] FDISK_COMMAND = [FDISK_PATH, "-l"] LS_COMMAND = ["ls", "-laR", "/"] ######################################################################## # Public functions ######################################################################## ########################### # executeAction() function ########################### # pylint: disable=W0613 def executeAction(configPath, options, config): """ Executes the sysinfo backup action. Args: configPath (String representing a path on disk): Path to configuration file on disk options (Options object): Program command-line options config (Config object): Program configuration Raises: ValueError: Under many generic error conditions IOError: If the backup process fails for some reason """ logger.debug("Executing sysinfo extended action.") if config.options is None or config.collect is None: raise ValueError("Cedar Backup configuration is not properly filled in.") _dumpDebianPackages(config.collect.targetDir, config.options.backupUser, config.options.backupGroup) _dumpPartitionTable(config.collect.targetDir, config.options.backupUser, config.options.backupGroup) _dumpFilesystemContents(config.collect.targetDir, config.options.backupUser, config.options.backupGroup) logger.info("Executed the sysinfo extended action successfully.") def _dumpDebianPackages(targetDir, backupUser, backupGroup, compress=True): """ Dumps a list of currently installed Debian packages via ``dpkg``. Args: targetDir: Directory to write output file into backupUser: User which should own the resulting file backupGroup: Group which should own the resulting file compress: Indicates whether to compress the output file Raises: IOError: If the dump fails for some reason """ if not os.path.exists(DPKG_PATH): logger.info("Not executing Debian package dump since %s doesn't seem to exist.", DPKG_PATH) elif not os.access(DPKG_PATH, os.X_OK): logger.info("Not executing Debian package dump since %s cannot be executed.", DPKG_PATH) else: (outputFile, filename) = _getOutputFile(targetDir, "dpkg-selections", compress) with outputFile: command = resolveCommand(DPKG_COMMAND) result = executeCommand(command, [], returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=outputFile)[0] if result != 0: raise IOError("Error [%d] executing Debian package dump." % result) if not os.path.exists(filename): raise IOError("File [%s] does not seem to exist after Debian package dump finished." % filename) changeOwnership(filename, backupUser, backupGroup) def _dumpPartitionTable(targetDir, backupUser, backupGroup, compress=True): """ Dumps information about the partition table via ``fdisk``. Args: targetDir: Directory to write output file into backupUser: User which should own the resulting file backupGroup: Group which should own the resulting file compress: Indicates whether to compress the output file Raises: IOError: If the dump fails for some reason """ if not os.path.exists(FDISK_PATH): logger.info("Not executing partition table dump since %s doesn't seem to exist.", FDISK_PATH) elif not os.access(FDISK_PATH, os.X_OK): logger.info("Not executing partition table dump since %s cannot be executed.", FDISK_PATH) else: (outputFile, filename) = _getOutputFile(targetDir, "fdisk-l", compress) with outputFile: command = resolveCommand(FDISK_COMMAND) result = executeCommand(command, [], returnOutput=False, ignoreStderr=True, outputFile=outputFile)[0] if result != 0: raise IOError("Error [%d] executing partition table dump." % result) if not os.path.exists(filename): raise IOError("File [%s] does not seem to exist after partition table dump finished." % filename) changeOwnership(filename, backupUser, backupGroup) def _dumpFilesystemContents(targetDir, backupUser, backupGroup, compress=True): """ Dumps complete listing of filesystem contents via ``ls -laR``. Args: targetDir: Directory to write output file into backupUser: User which should own the resulting file backupGroup: Group which should own the resulting file compress: Indicates whether to compress the output file Raises: IOError: If the dump fails for some reason """ (outputFile, filename) = _getOutputFile(targetDir, "ls-laR", compress) with outputFile: # Note: can't count on return status from 'ls', so we don't check it. command = resolveCommand(LS_COMMAND) executeCommand(command, [], returnOutput=False, ignoreStderr=True, doNotLog=True, outputFile=outputFile) if not os.path.exists(filename): raise IOError("File [%s] does not seem to exist after filesystem contents dump finished." % filename) changeOwnership(filename, backupUser, backupGroup) def _getOutputFile(targetDir, name, compress=True): """ Opens the output file used for saving a dump to the filesystem. The filename will be ``name.txt`` (or ``name.txt.bz2`` if ``compress`` is ``True``), written in the target directory. Args: targetDir: Target directory to write file in name: Name of the file to create compress: Indicates whether to write compressed output Returns: Tuple of (Output file object, filename), file opened in binary mode for use with executeCommand() """ filename = pathJoin(targetDir, "%s.txt" % name) if compress: filename = "%s.bz2" % filename logger.debug("Dump file will be [%s].", filename) if compress: outputFile = BZ2File(filename, "wb") else: outputFile = open(filename, "wb") return (outputFile, filename) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/filesystem.py0000644000000000000000000020053614567004737016736 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides filesystem-related objects. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides filesystem-related objects. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import hashlib import logging import math import os import re import tarfile from CedarBackup3.knapsack import alternateFit, bestFit, firstFit, worstFit from CedarBackup3.util import ( AbsolutePathList, RegexList, UnorderedList, calculateFileAge, dereferenceLink, displayBytes, encodePath, pathJoin, removeKeys, ) ######################################################################## # Module-wide variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.filesystem") ######################################################################## # FilesystemList class definition ######################################################################## class FilesystemList(list): ###################### # Class documentation ###################### """ Represents a list of filesystem items. This is a generic class that represents a list of filesystem items. Callers can add individual files or directories to the list, or can recursively add the contents of a directory. The class also allows for up-front exclusions in several forms (all files, all directories, all items matching a pattern, all items whose basename matches a pattern, or all directories containing a specific "ignore file"). Symbolic links are typically backed up non-recursively, i.e. the link to a directory is backed up, but not the contents of that link (we don't want to deal with recursive loops, etc.). The custom methods such as :any:`addFile` will only add items if they exist on the filesystem and do not match any exclusions that are already in place. However, since a FilesystemList is a subclass of Python's standard list class, callers can also add items to the list in the usual way, using methods like ``append()`` or ``insert()``. No validations apply to items added to the list in this way; however, many list-manipulation methods deal "gracefully" with items that don't exist in the filesystem, often by ignoring them. Once a list has been created, callers can remove individual items from the list using standard methods like ``pop()`` or ``remove()`` or they can use custom methods to remove specific types of entries or entries which match a particular pattern. *Note:* Regular expression patterns that apply to paths are assumed to be bounded at front and back by the beginning and end of the string, i.e. they are treated as if they begin with ``^`` and end with ``$``. This is true whether we are matching a complete path or a basename. """ ############## # Constructor ############## def __init__(self): """Initializes a list with no configured exclusions.""" list.__init__(self) self._excludeFiles = False self._excludeDirs = False self._excludeLinks = False self._excludePaths = None self._excludePatterns = None self._excludeBasenamePatterns = None self._ignoreFile = None self.excludeFiles = False self.excludeLinks = False self.excludeDirs = False self.excludePaths = [] self.excludePatterns = RegexList() self.excludeBasenamePatterns = RegexList() self.ignoreFile = None ############# # Properties ############# def _setExcludeFiles(self, value): """ Property target used to set the exclude files flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._excludeFiles = True else: self._excludeFiles = False def _getExcludeFiles(self): """ Property target used to get the exclude files flag. """ return self._excludeFiles def _setExcludeDirs(self, value): """ Property target used to set the exclude directories flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._excludeDirs = True else: self._excludeDirs = False def _getExcludeDirs(self): """ Property target used to get the exclude directories flag. """ return self._excludeDirs def _setExcludeLinks(self, value): """ Property target used to set the exclude soft links flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._excludeLinks = True else: self._excludeLinks = False def _getExcludeLinks(self): """ Property target used to get the exclude soft links flag. """ return self._excludeLinks def _setExcludePaths(self, value): """ Property target used to set the exclude paths list. A ``None`` value is converted to an empty list. Elements do not have to exist on disk at the time of assignment. Raises: ValueError: If any list element is not an absolute path """ self._excludePaths = AbsolutePathList() if value is not None: self._excludePaths.extend(value) def _getExcludePaths(self): """ Property target used to get the absolute exclude paths list. """ return self._excludePaths def _setExcludePatterns(self, value): """ Property target used to set the exclude patterns list. A ``None`` value is converted to an empty list. """ self._excludePatterns = RegexList() if value is not None: self._excludePatterns.extend(value) def _getExcludePatterns(self): """ Property target used to get the exclude patterns list. """ return self._excludePatterns def _setExcludeBasenamePatterns(self, value): """ Property target used to set the exclude basename patterns list. A ``None`` value is converted to an empty list. """ self._excludeBasenamePatterns = RegexList() if value is not None: self._excludeBasenamePatterns.extend(value) def _getExcludeBasenamePatterns(self): """ Property target used to get the exclude basename patterns list. """ return self._excludeBasenamePatterns def _setIgnoreFile(self, value): """ Property target used to set the ignore file. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The ignore file must be a non-empty string.") self._ignoreFile = value def _getIgnoreFile(self): """ Property target used to get the ignore file. """ return self._ignoreFile excludeFiles = property(_getExcludeFiles, _setExcludeFiles, None, "Boolean indicating whether files should be excluded.") excludeDirs = property(_getExcludeDirs, _setExcludeDirs, None, "Boolean indicating whether directories should be excluded.") excludeLinks = property(_getExcludeLinks, _setExcludeLinks, None, "Boolean indicating whether soft links should be excluded.") excludePaths = property(_getExcludePaths, _setExcludePaths, None, "List of absolute paths to be excluded.") excludePatterns = property( _getExcludePatterns, _setExcludePatterns, None, "List of regular expression patterns (matching complete path) to be excluded.", ) excludeBasenamePatterns = property( _getExcludeBasenamePatterns, _setExcludeBasenamePatterns, None, "List of regular expression patterns (matching basename) to be excluded.", ) ignoreFile = property(_getIgnoreFile, _setIgnoreFile, None, "Name of file which will cause directory contents to be ignored.") ############## # Add methods ############## def addFile(self, path): """ Adds a file to the list. The path must exist and must be a file or a link to an existing file. It will be added to the list subject to any exclusions that are in place. Args: path (String representing a path on disk): File path to be added to the list Returns: Number of items added to the list Raises: ValueError: If path is not a file or does not exist ValueError: If the path could not be encoded properly """ path = encodePath(path) path = normalizeFile(path) if not os.path.exists(path) or not os.path.isfile(path): logger.debug("Path [%s] is not a file or does not exist on disk.", path) raise ValueError("Path is not a file or does not exist on disk.") if self.excludeLinks and os.path.islink(path): logger.debug("Path [%s] is excluded based on excludeLinks.", path) return 0 if self.excludeFiles: logger.debug("Path [%s] is excluded based on excludeFiles.", path) return 0 if path in self.excludePaths: logger.debug("Path [%s] is excluded based on excludePaths.", path) return 0 for pattern in self.excludePatterns: pattern = encodePath(pattern) # use same encoding as filenames if re.compile(r"^%s$" % pattern).match(path): # safe to assume all are valid due to RegexList logger.debug("Path [%s] is excluded based on pattern [%s].", path, pattern) return 0 for pattern in self.excludeBasenamePatterns: # safe to assume all are valid due to RegexList pattern = encodePath(pattern) # use same encoding as filenames if re.compile(r"^%s$" % pattern).match(os.path.basename(path)): logger.debug("Path [%s] is excluded based on basename pattern [%s].", path, pattern) return 0 self.append(path) logger.debug("Added file to list: [%s]", path) return 1 def addDir(self, path): """ Adds a directory to the list. The path must exist and must be a directory or a link to an existing directory. It will be added to the list subject to any exclusions that are in place. The :any:`ignoreFile` does not apply to this method, only to :any:`addDirContents`. Args: path (String representing a path on disk): Directory path to be added to the list Returns: Number of items added to the list Raises: ValueError: If path is not a directory or does not exist ValueError: If the path could not be encoded properly """ path = encodePath(path) path = normalizeDir(path) if not os.path.exists(path) or not os.path.isdir(path): logger.debug("Path [%s] is not a directory or does not exist on disk.", path) raise ValueError("Path is not a directory or does not exist on disk.") if self.excludeLinks and os.path.islink(path): logger.debug("Path [%s] is excluded based on excludeLinks.", path) return 0 if self.excludeDirs: logger.debug("Path [%s] is excluded based on excludeDirs.", path) return 0 if path in self.excludePaths: logger.debug("Path [%s] is excluded based on excludePaths.", path) return 0 for pattern in self.excludePatterns: # safe to assume all are valid due to RegexList pattern = encodePath(pattern) # use same encoding as filenames if re.compile(r"^%s$" % pattern).match(path): logger.debug("Path [%s] is excluded based on pattern [%s].", path, pattern) return 0 for pattern in self.excludeBasenamePatterns: # safe to assume all are valid due to RegexList pattern = encodePath(pattern) # use same encoding as filenames if re.compile(r"^%s$" % pattern).match(os.path.basename(path)): logger.debug("Path [%s] is excluded based on basename pattern [%s].", path, pattern) return 0 self.append(path) logger.debug("Added directory to list: [%s]", path) return 1 def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0, dereference=False): """ Adds the contents of a directory to the list. The path must exist and must be a directory or a link to a directory. The contents of the directory (as well as the directory path itself) will be recursively added to the list, subject to any exclusions that are in place. If you only want the directory and its immediate contents to be added, then pass in ``recursive=False``. *Note:* If a directory's absolute path matches an exclude pattern or path, or if the directory contains the configured ignore file, then the directory and all of its contents will be recursively excluded from the list. *Note:* If the passed-in directory happens to be a soft link, it will be recursed. However, the linkDepth parameter controls whether any soft links *within* the directory will be recursed. The link depth is maximum depth of the tree at which soft links should be followed. So, a depth of 0 does not follow any soft links, a depth of 1 follows only links within the passed-in directory, a depth of 2 follows the links at the next level down, etc. *Note:* Any invalid soft links (i.e. soft links that point to non-existent items) will be silently ignored. *Note:* The :any:`excludeDirs` flag only controls whether any given directory path itself is added to the list once it has been discovered. It does *not* modify any behavior related to directory recursion. *Note:* If you call this method *on a link to a directory* that link will never be dereferenced (it may, however, be followed). Args: path (String representing a path on disk): Directory path whose contents should be added to the list recursive (Boolean value): Indicates whether directory contents should be added recursively addSelf (Boolean value): Indicates whether the directory itself should be added to the list linkDepth (Integer value): Maximum depth of the tree at which soft links should be followed, zero means not to folow dereference (Boolean value): Indicates whether soft links, if followed, should be dereferenced Returns: Number of items recursively added to the list Raises: ValueError: If path is not a directory or does not exist ValueError: If the path could not be encoded properly """ path = encodePath(path) path = normalizeDir(path) return self._addDirContentsInternal(path, addSelf, recursive, linkDepth, dereference) def _addDirContentsInternal(self, path, includePath=True, recursive=True, linkDepth=0, dereference=False): """ Internal implementation of ``addDirContents``. This internal implementation exists due to some refactoring. Basically, some subclasses have a need to add the contents of a directory, but not the directory itself. This is different than the standard ``FilesystemList`` behavior and actually ends up making a special case out of the first call in the recursive chain. Since I don't want to expose the modified interface, ``addDirContents`` ends up being wholly implemented in terms of this method. The linkDepth parameter controls whether soft links are followed when we are adding the contents recursively. Any recursive calls reduce the value by one. If the value zero or less, then soft links will just be added as directories, but will not be followed. This means that links are followed to a *constant depth* starting from the top-most directory. There is one difference between soft links and directories: soft links that are added recursively are not placed into the list explicitly. This is because if we do add the links recursively, the resulting tar file gets a little confused (it has a link and a directory with the same name). *Note:* If you call this method *on a link to a directory* that link will never be dereferenced (it may, however, be followed). Args: path: Directory path whose contents should be added to the list includePath: Indicates whether to include the path as well as contents recursive: Indicates whether directory contents should be added recursively linkDepth: Depth of soft links that should be followed dereference: Indicates whether soft links, if followed, should be dereferenced Returns: Number of items recursively added to the list Raises: ValueError: If path is not a directory or does not exist """ added = 0 if not os.path.exists(path) or not os.path.isdir(path): logger.debug("Path [%s] is not a directory or does not exist on disk.", path) raise ValueError("Path is not a directory or does not exist on disk.") if path in self.excludePaths: logger.debug("Path [%s] is excluded based on excludePaths.", path) return added for pattern in self.excludePatterns: # safe to assume all are valid due to RegexList pattern = encodePath(pattern) # use same encoding as filenames if re.compile(r"^%s$" % pattern).match(path): logger.debug("Path [%s] is excluded based on pattern [%s].", path, pattern) return added for pattern in self.excludeBasenamePatterns: # safe to assume all are valid due to RegexList pattern = encodePath(pattern) # use same encoding as filenames if re.compile(r"^%s$" % pattern).match(os.path.basename(path)): logger.debug("Path [%s] is excluded based on basename pattern [%s].", path, pattern) return added if self.ignoreFile is not None and os.path.exists(pathJoin(path, self.ignoreFile)): logger.debug("Path [%s] is excluded based on ignore file.", path) return added if includePath: added += self.addDir(path) # could actually be excluded by addDir, yet for entry in os.listdir(path): entrypath = pathJoin(path, entry) if os.path.isfile(entrypath): if linkDepth > 0 and dereference: derefpath = dereferenceLink(entrypath) if derefpath != entrypath: added += self.addFile(derefpath) added += self.addFile(entrypath) elif os.path.isdir(entrypath): if os.path.islink(entrypath): if recursive: if linkDepth > 0: newDepth = linkDepth - 1 if dereference: derefpath = dereferenceLink(entrypath) if derefpath != entrypath: added += self._addDirContentsInternal(derefpath, True, recursive, newDepth, dereference) added += self.addDir(entrypath) else: added += self._addDirContentsInternal(entrypath, False, recursive, newDepth, dereference) else: added += self.addDir(entrypath) else: added += self.addDir(entrypath) else: if recursive: newDepth = linkDepth - 1 added += self._addDirContentsInternal(entrypath, True, recursive, newDepth, dereference) else: added += self.addDir(entrypath) return added ################# # Remove methods ################# def removeFiles(self, pattern=None): """ Removes file entries from the list. If ``pattern`` is not passed in or is ``None``, then all file entries will be removed from the list. Otherwise, only those file entries matching the pattern will be removed. Any entry which does not exist on disk will be ignored (use :any:`removeInvalid` to purge those entries). This method might be fairly slow for large lists, since it must check the type of each item in the list. If you know ahead of time that you want to exclude all files, then you will be better off setting :any:`excludeFiles` to ``True`` before adding items to the list. Args: pattern: Regular expression pattern representing entries to remove Returns: Number of entries removed Raises: ValueError: If the passed-in pattern is not a valid regular expression """ removed = 0 if pattern is None: for entry in self[:]: if os.path.exists(entry) and os.path.isfile(entry): self.remove(entry) logger.debug("Removed path [%s] from list.", entry) removed += 1 else: try: pattern = encodePath(pattern) # use same encoding as filenames compiled = re.compile(pattern) except re.error: raise ValueError("Pattern is not a valid regular expression.") for entry in self[:]: if os.path.exists(entry) and os.path.isfile(entry): if compiled.match(entry): self.remove(entry) logger.debug("Removed path [%s] from list.", entry) removed += 1 logger.debug("Removed a total of %d entries.", removed) return removed def removeDirs(self, pattern=None): """ Removes directory entries from the list. If ``pattern`` is not passed in or is ``None``, then all directory entries will be removed from the list. Otherwise, only those directory entries matching the pattern will be removed. Any entry which does not exist on disk will be ignored (use :any:`removeInvalid` to purge those entries). This method might be fairly slow for large lists, since it must check the type of each item in the list. If you know ahead of time that you want to exclude all directories, then you will be better off setting :any:`excludeDirs` to ``True`` before adding items to the list (note that this will not prevent you from recursively adding the *contents* of directories). Args: pattern: Regular expression pattern representing entries to remove Returns: Number of entries removed Raises: ValueError: If the passed-in pattern is not a valid regular expression """ removed = 0 if pattern is None: for entry in self[:]: if os.path.exists(entry) and os.path.isdir(entry): self.remove(entry) logger.debug("Removed path [%s] from list.", entry) removed += 1 else: try: pattern = encodePath(pattern) # use same encoding as filenames compiled = re.compile(pattern) except re.error: raise ValueError("Pattern is not a valid regular expression.") for entry in self[:]: if os.path.exists(entry) and os.path.isdir(entry): if compiled.match(entry): self.remove(entry) logger.debug("Removed path [%s] from list based on pattern [%s].", entry, pattern) removed += 1 logger.debug("Removed a total of %d entries.", removed) return removed def removeLinks(self, pattern=None): """ Removes soft link entries from the list. If ``pattern`` is not passed in or is ``None``, then all soft link entries will be removed from the list. Otherwise, only those soft link entries matching the pattern will be removed. Any entry which does not exist on disk will be ignored (use :any:`removeInvalid` to purge those entries). This method might be fairly slow for large lists, since it must check the type of each item in the list. If you know ahead of time that you want to exclude all soft links, then you will be better off setting :any:`excludeLinks` to ``True`` before adding items to the list. Args: pattern: Regular expression pattern representing entries to remove Returns: Number of entries removed Raises: ValueError: If the passed-in pattern is not a valid regular expression """ removed = 0 if pattern is None: for entry in self[:]: if os.path.exists(entry) and os.path.islink(entry): self.remove(entry) logger.debug("Removed path [%s] from list.", entry) removed += 1 else: try: pattern = encodePath(pattern) # use same encoding as filenames compiled = re.compile(pattern) except re.error: raise ValueError("Pattern is not a valid regular expression.") for entry in self[:]: if os.path.exists(entry) and os.path.islink(entry): if compiled.match(entry): self.remove(entry) logger.debug("Removed path [%s] from list based on pattern [%s].", entry, pattern) removed += 1 logger.debug("Removed a total of %d entries.", removed) return removed def removeMatch(self, pattern): """ Removes from the list all entries matching a pattern. This method removes from the list all entries which match the passed in ``pattern``. Since there is no need to check the type of each entry, it is faster to call this method than to call the :any:`removeFiles`, :any:`removeDirs` or :any:`removeLinks` methods individually. If you know which patterns you will want to remove ahead of time, you may be better off setting :any:`excludePatterns` or :any:`excludeBasenamePatterns` before adding items to the list. *Note:* Unlike when using the exclude lists, the pattern here is *not* bounded at the front and the back of the string. You can use any pattern you want. Args: pattern: Regular expression pattern representing entries to remove Returns: Number of entries removed Raises: ValueError: If the passed-in pattern is not a valid regular expression """ try: pattern = encodePath(pattern) # use same encoding as filenames compiled = re.compile(pattern) except re.error: raise ValueError("Pattern is not a valid regular expression.") removed = 0 for entry in self[:]: if compiled.match(entry): self.remove(entry) logger.debug("Removed path [%s] from list based on pattern [%s].", entry, pattern) removed += 1 logger.debug("Removed a total of %d entries.", removed) return removed def removeInvalid(self): """ Removes from the list all entries that do not exist on disk. This method removes from the list all entries which do not currently exist on disk in some form. No attention is paid to whether the entries are files or directories. Returns: Number of entries removed """ removed = 0 for entry in self[:]: if not os.path.exists(entry): self.remove(entry) logger.debug("Removed path [%s] from list.", entry) removed += 1 logger.debug("Removed a total of %d entries.", removed) return removed ################## # Utility methods ################## def normalize(self): """Normalizes the list, ensuring that each entry is unique.""" orig = len(self) self.sort() dups = list(filter(lambda x, self=self: self[x] == self[x + 1], list(range(0, len(self) - 1)))) items = list(map(lambda x, self=self: self[x], dups)) list(map(self.remove, items)) new = len(self) logger.debug("Completed normalizing list; removed %d items (%d originally, %d now).", new - orig, orig, new) def verify(self): """ Verifies that all entries in the list exist on disk. Returns: ``True`` if all entries exist, ``False`` otherwise """ for entry in self: if not os.path.exists(entry): logger.debug("Path [%s] is invalid; list is not valid.", entry) return False logger.debug("All entries in list are valid.") return True ######################################################################## # SpanItem class definition ######################################################################## class SpanItem(object): # pylint: disable=R0903 """ Item returned by :any:`BackupFileList.generateSpan`. """ def __init__(self, fileList, size, capacity, utilization): """ Create object. Args: fileList: List of files size: Size (in bytes) of files utilization: Utilization, as a percentage (0-100) """ self.fileList = fileList self.size = size self.capacity = capacity self.utilization = utilization ######################################################################## # BackupFileList class definition ######################################################################## class BackupFileList(FilesystemList): # pylint: disable=R0904 ###################### # Class documentation ###################### """ List of files to be backed up. A BackupFileList is a :any:`FilesystemList` containing a list of files to be backed up. It only contains files, not directories (soft links are treated like files). On top of the generic functionality provided by :any:`FilesystemList`, this class adds functionality to keep a hash (checksum) for each file in the list, and it also provides a method to calculate the total size of the files in the list and a way to export the list into tar form. """ ############## # Constructor ############## def __init__(self): """Initializes a list with no configured exclusions.""" FilesystemList.__init__(self) ################################ # Overridden superclass methods ################################ def addDir(self, path): """ Adds a directory to the list. Note that this class does not allow directories to be added by themselves (a backup list contains only files). However, since links to directories are technically files, we allow them to be added. This method is implemented in terms of the superclass method, with one additional validation: the superclass method is only called if the passed-in path is both a directory and a link. All of the superclass's existing validations and restrictions apply. Args: path (String representing a path on disk): Directory path to be added to the list Returns: Number of items added to the list Raises: ValueError: If path is not a directory or does not exist ValueError: If the path could not be encoded properly """ path = encodePath(path) path = normalizeDir(path) if os.path.isdir(path) and not os.path.islink(path): return 0 else: return FilesystemList.addDir(self, path) ################## # Utility methods ################## def totalSize(self): """ Returns the total size among all files in the list. Only files are counted. Soft links that point at files are ignored. Entries which do not exist on disk are ignored. Returns: Total size, in bytes """ total = 0.0 for entry in self: if os.path.isfile(entry) and not os.path.islink(entry): total += float(os.stat(entry).st_size) return total def generateSizeMap(self): """ Generates a mapping from file to file size in bytes. The mapping does include soft links, which are listed with size zero. Entries which do not exist on disk are ignored. Returns: Dictionary mapping file to file size """ table = {} for entry in self: if os.path.islink(entry): table[entry] = 0.0 elif os.path.isfile(entry): table[entry] = float(os.stat(entry).st_size) return table def generateDigestMap(self, stripPrefix=None): """ Generates a mapping from file to file digest. Currently, the digest is an SHA hash, which should be pretty secure. In the future, this might be a different kind of hash, but we guarantee that the type of the hash will not change unless the library major version number is bumped. Entries which do not exist on disk are ignored. Soft links are ignored. We would end up generating a digest for the file that the soft link points at, which doesn't make any sense. If ``stripPrefix`` is passed in, then that prefix will be stripped from each key when the map is generated. This can be useful in generating two "relative" digest maps to be compared to one another. Args: stripPrefix (String with any contents): Common prefix to be stripped from paths Returns: Dictionary mapping file to digest value @see: :any:`removeUnchanged` """ table = {} if stripPrefix is not None: for entry in self: if os.path.isfile(entry) and not os.path.islink(entry): table[entry.replace(stripPrefix, "", 1)] = BackupFileList._generateDigest(entry) else: for entry in self: if os.path.isfile(entry) and not os.path.islink(entry): table[entry] = BackupFileList._generateDigest(entry) return table @staticmethod def _generateDigest(path): """ Generates an SHA digest for a given file on disk. The original code for this function used this simplistic implementation, which requires reading the entire file into memory at once in order to generate a digest value:: sha.new(open(path).read()).hexdigest() Not surprisingly, this isn't an optimal solution. The U{Simple file hashing } Python Cookbook recipe describes how to incrementally generate a hash value by reading in chunks of data rather than reading the file all at once. The recipe relies on the the ``update()`` method of the various Python hashing algorithms. In my tests using a 110 MB file on CD, the original implementation requires 111 seconds. This implementation requires only 40-45 seconds, which is a pretty substantial speed-up. Experience shows that reading in around 4kB (4096 bytes) at a time yields the best performance. Smaller reads are quite a bit slower, and larger reads don't make much of a difference. The 4kB number makes me a little suspicious, and I think it might be related to the size of a filesystem read at the hardware level. However, I've decided to just hardcode 4096 until I have evidence that shows it's worthwhile making the read size configurable. Args: path: Path to generate digest for Returns: ASCII-safe SHA digest for the file Raises: OSError: If the file cannot be opened """ # pylint: disable=C0103,E1101 s = hashlib.sha1() with open(path, mode="rb") as f: readBytes = 4096 # see notes above while readBytes > 0: readString = f.read(readBytes) s.update(readString) readBytes = len(readString) digest = s.hexdigest() logger.debug("Generated digest [%s] for file [%s].", digest, path) return digest def generateFitted(self, capacity, algorithm="worst_fit"): """ Generates a list of items that fit in the indicated capacity. Sometimes, callers would like to include every item in a list, but are unable to because not all of the items fit in the space available. This method returns a copy of the list, containing only the items that fit in a given capacity. A copy is returned so that we don't lose any information if for some reason the fitted list is unsatisfactory. The fitting is done using the functions in the knapsack module. By default, the first fit algorithm is used, but you can also choose from best fit, worst fit and alternate fit. Args: capacity (Integer, in bytes): Maximum capacity among the files in the new list algorithm (One of "first_fit", "best_fit", "worst_fit", "alternate_fit"): Knapsack (fit) algorithm to use Returns: Copy of list with total size no larger than indicated capacity Raises: ValueError: If the algorithm is invalid """ table = self._getKnapsackTable() function = BackupFileList._getKnapsackFunction(algorithm) return function(table, capacity)[0] def generateSpan(self, capacity, algorithm="worst_fit"): """ Splits the list of items into sub-lists that fit in a given capacity. Sometimes, callers need split to a backup file list into a set of smaller lists. For instance, you could use this to "span" the files across a set of discs. The fitting is done using the functions in the knapsack module. By default, the first fit algorithm is used, but you can also choose from best fit, worst fit and alternate fit. *Note:* If any of your items are larger than the capacity, then it won't be possible to find a solution. In this case, a value error will be raised. Args: capacity (Integer, in bytes): Maximum capacity among the files in the new list algorithm (One of "first_fit", "best_fit", "worst_fit", "alternate_fit"): Knapsack (fit) algorithm to use Returns: List of :any:`SpanItem` objects Raises: ValueError: If the algorithm is invalid ValueError: If it's not possible to fit some items """ spanItems = [] function = BackupFileList._getKnapsackFunction(algorithm) table = self._getKnapsackTable(capacity) iteration = 0 while len(table) > 0: iteration += 1 fit = function(table, capacity) if len(fit[0]) == 0: # Should never happen due to validations in _convertToKnapsackForm(), but let's be safe raise ValueError("After iteration %d, unable to add any new items." % iteration) removeKeys(table, fit[0]) utilization = (float(fit[1]) / float(capacity)) * 100.0 item = SpanItem(fit[0], fit[1], capacity, utilization) spanItems.append(item) return spanItems def _getKnapsackTable(self, capacity=None): """ Converts the list into the form needed by the knapsack algorithms. Returns: Dictionary mapping file name to tuple of (file path, file size) """ table = {} for entry in self: if os.path.islink(entry): table[entry] = (entry, 0.0) elif os.path.isfile(entry): size = float(os.stat(entry).st_size) if capacity is not None: if size > capacity: raise ValueError("File [%s] cannot fit in capacity %s." % (entry, displayBytes(capacity))) table[entry] = (entry, size) return table @staticmethod def _getKnapsackFunction(algorithm): """ Returns a reference to the function associated with an algorithm name. Algorithm name must be one of "first_fit", "best_fit", "worst_fit", "alternate_fit" Args: algorithm: Name of the algorithm Returns: Reference to knapsack function Raises: ValueError: If the algorithm name is unknown """ if algorithm == "first_fit": return firstFit elif algorithm == "best_fit": return bestFit elif algorithm == "worst_fit": return worstFit elif algorithm == "alternate_fit": return alternateFit else: raise ValueError("Algorithm [%s] is invalid." % algorithm) def generateTarfile(self, path, mode="tar", ignore=False, flat=False): """ Creates a tar file containing the files in the list. By default, this method will create uncompressed tar files. If you pass in mode ``'targz'``, then it will create gzipped tar files, and if you pass in mode ``'tarbz2'``, then it will create bzipped tar files. The tar file will be created as a GNU tar archive, which enables extended file name lengths, etc. Since GNU tar is so prevalent, I've decided that the extra functionality out-weighs the disadvantage of not being "standard". If you pass in ``flat=True``, then a "flat" archive will be created, and all of the files will be added to the root of the archive. So, the file ``/tmp/something/whatever.txt`` would be added as just ``whatever.txt``. By default, the whole method call fails if there are problems adding any of the files to the archive, resulting in an exception. Under these circumstances, callers are advised that they might want to call :any:`removeInvalid` and then attempt to extract the tar file a second time, since the most common cause of failures is a missing file (a file that existed when the list was built, but is gone again by the time the tar file is built). If you want to, you can pass in ``ignore=True``, and the method will ignore errors encountered when adding individual files to the archive (but not errors opening and closing the archive itself). We'll always attempt to remove the tarfile from disk if an exception will be thrown. *Note:* No validation is done as to whether the entries in the list are files, since only files or soft links should be in an object like this. However, to be safe, everything is explicitly added to the tar archive non-recursively so it's safe to include soft links to directories. *Note:* The Python ``tarfile`` module, which is used internally here, is supposed to deal properly with long filenames and links. In my testing, I have found that it appears to be able to add long really long filenames to archives, but doesn't do a good job reading them back out, even out of an archive it created. Fortunately, all Cedar Backup does is add files to archives. Args: path (String representing a path on disk): Path of tar file to create on disk mode (One of either ``'tar'``, ``'targz'`` or ``'tarbz2'``): Tar creation mode ignore (Boolean): Indicates whether to ignore certain errors flat (Boolean): Creates "flat" archive by putting all items in root Raises: ValueError: If mode is not valid ValueError: If list is empty ValueError: If the path could not be encoded properly TarError: If there is a problem creating the tar file """ # pylint: disable=E1101 path = encodePath(path) if len(self) == 0: raise ValueError("Empty list cannot be used to generate tarfile.") if mode == "tar": tarmode = "w:" elif mode == "targz": tarmode = "w:gz" elif mode == "tarbz2": tarmode = "w:bz2" else: raise ValueError("Mode [%s] is not valid." % mode) try: tar = tarfile.open(path, tarmode) try: tar.format = tarfile.GNU_FORMAT except AttributeError: tar.posix = False for entry in self: try: if flat: tar.add(entry, arcname=os.path.basename(entry), recursive=False) else: tar.add(entry, recursive=False) except tarfile.TarError as e: if not ignore: raise e logger.info("Unable to add file [%s]; going on anyway.", entry) except OSError as e: if not ignore: raise tarfile.TarError(e) logger.info("Unable to add file [%s]; going on anyway.", entry) tar.close() except tarfile.ReadError as e: try: tar.close() except: pass if os.path.exists(path): try: os.remove(path) except: pass raise tarfile.ReadError("Unable to open [%s]; maybe directory doesn't exist?" % path) except tarfile.TarError as e: try: tar.close() except: pass if os.path.exists(path): try: os.remove(path) except: pass raise e def removeUnchanged(self, digestMap, captureDigest=False): """ Removes unchanged entries from the list. This method relies on a digest map as returned from :any:`generateDigestMap`. For each entry in ``digestMap``, if the entry also exists in the current list *and* the entry in the current list has the same digest value as in the map, the entry in the current list will be removed. This method offers a convenient way for callers to filter unneeded entries from a list. The idea is that a caller will capture a digest map from ``generateDigestMap`` at some point in time (perhaps the beginning of the week), and will save off that map using ``pickle`` or some other method. Then, the caller could use this method sometime in the future to filter out any unchanged files based on the saved-off map. If ``captureDigest`` is passed-in as ``True``, then digest information will be captured for the entire list before the removal step occurs using the same rules as in :any:`generateDigestMap`. The check will involve a lookup into the complete digest map. If ``captureDigest`` is passed in as ``False``, we will only generate a digest value for files we actually need to check, and we'll ignore any entry in the list which isn't a file that currently exists on disk. The return value varies depending on ``captureDigest``, as well. To preserve backwards compatibility, if ``captureDigest`` is ``False``, then we'll just return a single value representing the number of entries removed. Otherwise, we'll return a tuple of C{(entries removed, digest map)}. The returned digest map will be in exactly the form returned by :any:`generateDigestMap`. *Note:* For performance reasons, this method actually ends up rebuilding the list from scratch. First, we build a temporary dictionary containing all of the items from the original list. Then, we remove items as needed from the dictionary (which is faster than the equivalent operation on a list). Finally, we replace the contents of the current list based on the keys left in the dictionary. This should be transparent to the caller. Args: digestMap (Map as returned from :any:`generateDigestMap`): Dictionary mapping file name to digest value captureDigest (Boolean): Indicates that digest information should be captured Returns: Results as discussed above (format varies based on arguments) """ if captureDigest: removed = 0 table = {} captured = {} for entry in self: if os.path.isfile(entry) and not os.path.islink(entry): table[entry] = BackupFileList._generateDigest(entry) captured[entry] = table[entry] else: table[entry] = None for entry in list(digestMap.keys()): if entry in table: if table[entry] is not None: # equivalent to file/link check in other case digest = table[entry] if digest == digestMap[entry]: removed += 1 del table[entry] logger.debug("Discarded unchanged file [%s].", entry) self[:] = list(table.keys()) return (removed, captured) else: removed = 0 table = {} for entry in self: table[entry] = None for entry in list(digestMap.keys()): if entry in table: if os.path.isfile(entry) and not os.path.islink(entry): digest = BackupFileList._generateDigest(entry) if digest == digestMap[entry]: removed += 1 del table[entry] logger.debug("Discarded unchanged file [%s].", entry) self[:] = list(table.keys()) return removed ######################################################################## # PurgeItemList class definition ######################################################################## class PurgeItemList(FilesystemList): # pylint: disable=R0904 ###################### # Class documentation ###################### """ List of files and directories to be purged. A PurgeItemList is a :any:`FilesystemList` containing a list of files and directories to be purged. On top of the generic functionality provided by :any:`FilesystemList`, this class adds functionality to remove items that are too young to be purged, and to actually remove each item in the list from the filesystem. The other main difference is that when you add a directory's contents to a purge item list, the directory itself is not added to the list. This way, if someone asks to purge within in ``/opt/backup/collect``, that directory doesn't get removed once all of the files within it is gone. """ ############## # Constructor ############## def __init__(self): """Initializes a list with no configured exclusions.""" FilesystemList.__init__(self) ############## # Add methods ############## def addDirContents(self, path, recursive=True, addSelf=True, linkDepth=0, dereference=False): """ Adds the contents of a directory to the list. The path must exist and must be a directory or a link to a directory. The contents of the directory (but *not* the directory path itself) will be recursively added to the list, subject to any exclusions that are in place. If you only want the directory and its contents to be added, then pass in ``recursive=False``. *Note:* If a directory's absolute path matches an exclude pattern or path, or if the directory contains the configured ignore file, then the directory and all of its contents will be recursively excluded from the list. *Note:* If the passed-in directory happens to be a soft link, it will be recursed. However, the linkDepth parameter controls whether any soft links *within* the directory will be recursed. The link depth is maximum depth of the tree at which soft links should be followed. So, a depth of 0 does not follow any soft links, a depth of 1 follows only links within the passed-in directory, a depth of 2 follows the links at the next level down, etc. *Note:* Any invalid soft links (i.e. soft links that point to non-existent items) will be silently ignored. *Note:* The :any:`excludeDirs` flag only controls whether any given soft link path itself is added to the list once it has been discovered. It does *not* modify any behavior related to directory recursion. *Note:* The :any:`excludeDirs` flag only controls whether any given directory path itself is added to the list once it has been discovered. It does *not* modify any behavior related to directory recursion. *Note:* If you call this method *on a link to a directory* that link will never be dereferenced (it may, however, be followed). Args: path (String representing a path on disk): Directory path whose contents should be added to the list recursive (Boolean value): Indicates whether directory contents should be added recursively addSelf: Ignored in this subclass linkDepth (Integer value, where zero means not to follow any soft links): Depth of soft links that should be followed dereference (Boolean value): Indicates whether soft links, if followed, should be dereferenced Returns: Number of items recursively added to the list Raises: ValueError: If path is not a directory or does not exist ValueError: If the path could not be encoded properly """ path = encodePath(path) path = normalizeDir(path) return super(PurgeItemList, self)._addDirContentsInternal(path, False, recursive, linkDepth, dereference) ################## # Utility methods ################## def removeYoungFiles(self, daysOld): """ Removes from the list files younger than a certain age (in days). Any file whose "age" in days is less than (``<``) the value of the ``daysOld`` parameter will be removed from the list so that it will not be purged later when :any:`purgeItems` is called. Directories and soft links will be ignored. The "age" of a file is the amount of time since the file was last used, per the most recent of the file's ``st_atime`` and ``st_mtime`` values. *Note:* Some people find the "sense" of this method confusing or "backwards". Keep in mind that this method is used to remove items *from the list*, not from the filesystem! It removes from the list those items that you would *not* want to purge because they are too young. As an example, passing in ``daysOld`` of zero (0) would remove from the list no files, which would result in purging all of the files later. I would be happy to make a synonym of this method with an easier-to-understand "sense", if someone can suggest one. Args: daysOld (Integer value >= 0): Minimum age of files that are to be kept in the list Returns: Number of entries removed """ removed = 0 daysOld = int(daysOld) if daysOld < 0: raise ValueError("Days old value must be an integer >= 0.") for entry in self[:]: if os.path.isfile(entry) and not os.path.islink(entry): try: ageInDays = calculateFileAge(entry) ageInWholeDays = math.floor(ageInDays) if ageInWholeDays < 0: ageInWholeDays = 0 if ageInWholeDays < daysOld: removed += 1 self.remove(entry) except OSError: pass return removed def purgeItems(self): """ Purges all items in the list. Every item in the list will be purged. Directories in the list will *not* be purged recursively, and hence will only be removed if they are empty. Errors will be ignored. To faciliate easy removal of directories that will end up being empty, the delete process happens in two passes: files first (including soft links), then directories. Returns: Tuple containing count of (files, dirs) removed """ files = 0 dirs = 0 for entry in self: if os.path.exists(entry) and (os.path.isfile(entry) or os.path.islink(entry)): try: os.remove(entry) files += 1 logger.debug("Purged file [%s].", entry) except OSError: pass for entry in self: if os.path.exists(entry) and os.path.isdir(entry) and not os.path.islink(entry): try: os.rmdir(entry) dirs += 1 logger.debug("Purged empty directory [%s].", entry) except OSError: pass return (files, dirs) ######################################################################## # Public functions ######################################################################## ########################### # normalizeFile() function ########################### def normalizeFile(path): """ Normalizes a file name. On Windows in particular, we often end up with mixed slashes, where parts of a path have forward slash and parts have backward slash. This makes it difficult to construct exclusions in configuration, because you never know what part of a path will have what kind of slash. I've decided to standardize on forward slashes. Args: path (String representing a path on disk): Path to be normalized Returns: Normalized path, which should be equivalent to the original """ return path.replace("\\", "/") ########################## # normalizeDir() function ########################## def normalizeDir(path): """ Normalizes a directory name. For our purposes, a directory name is normalized by removing the trailing path separator, if any. This is important because we want directories to appear within lists in a consistent way, although from the user's perspective passing in ``/path/to/dir/`` and ``/path/to/dir`` are equivalent. We also convert slashes. On Windows in particular, we often end up with mixed slashes, where parts of a path have forward slash and parts have backward slash. This makes it difficult to construct exclusions in configuration, because you never know what part of a path will have what kind of slash. I've decided to standardize on forward slashes. Args: path (String representing a path on disk): Path to be normalized Returns: Normalized path, which should be equivalent to the original """ if path != os.sep and path[-1:] == os.sep: return path[:-1] return path.replace("\\", "/") ############################# # compareContents() function ############################# def compareContents(path1, path2, verbose=False): """ Compares the contents of two directories to see if they are equivalent. The two directories are recursively compared. First, we check whether they contain exactly the same set of files. Then, we check to see every given file has exactly the same contents in both directories. This is all relatively simple to implement through the magic of :any:`BackupFileList.generateDigestMap`, which knows how to strip a path prefix off the front of each entry in the mapping it generates. This makes our comparison as simple as creating a list for each path, then generating a digest map for each path and comparing the two. If no exception is thrown, the two directories are considered identical. If the ``verbose`` flag is ``True``, then an alternate (but slower) method is used so that any thrown exception can indicate exactly which file caused the comparison to fail. The thrown ``ValueError`` exception distinguishes between the directories containing different files, and containing the same files with differing content. *Note:* Symlinks are *not* followed for the purposes of this comparison. Args: path1 (String representing a path on disk): First path to compare path2 (String representing a path on disk): First path to compare verbose (Boolean): Indicates whether a verbose response should be given Raises: ValueError: If a directory doesn't exist or can't be read ValueError: If the two directories are not equivalent IOError: If there is an unusual problem reading the directories """ try: path1List = BackupFileList() path1List.addDirContents(path1) path1Digest = path1List.generateDigestMap(stripPrefix=normalizeDir(path1)) path2List = BackupFileList() path2List.addDirContents(path2) path2Digest = path2List.generateDigestMap(stripPrefix=normalizeDir(path2)) compareDigestMaps(path1Digest, path2Digest, verbose) except IOError as e: logger.error("I/O error encountered during consistency check.") raise e def compareDigestMaps(digest1, digest2, verbose=False): """ Compares two digest maps and throws an exception if they differ. Args: digest1 (Digest as returned from BackupFileList.generateDigestMap()): First digest to compare digest2 (Digest as returned from BackupFileList.generateDigestMap()): Second digest to compare verbose (Boolean): Indicates whether a verbose response should be given Raises: ValueError: If the two directories are not equivalent """ if not verbose: if digest1 != digest2: raise ValueError("Consistency check failed.") else: list1 = UnorderedList(list(digest1.keys())) list2 = UnorderedList(list(digest2.keys())) if list1 != list2: raise ValueError("Directories contain a different set of files.") for key in list1: if digest1[key] != digest2[key]: raise ValueError("File contents for [%s] vary between directories." % key) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/image.py0000644000000000000000000000245614567004737015635 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides interface backwards compatibility. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides interface backwards compatibility. In Cedar Backup 2.10.0, a refactoring effort took place while adding code to support DVD hardware. All of the writer functionality was moved to the writers/ package. This mostly-empty file remains to preserve the Cedar Backup library interface. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## from CedarBackup3.writers.util import IsoImage # pylint: disable=W0611 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/knapsack.py0000644000000000000000000003223714567004737016346 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2005,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides knapsack algorithms used for "fit" decisions # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######## # Notes ######## """ Provides the implementation for various knapsack algorithms. Knapsack algorithms are "fit" algorithms, used to take a set of "things" and decide on the optimal way to fit them into some container. The focus of this code is to fit files onto a disc, although the interface (in terms of item, item size and capacity size, with no units) is generic enough that it can be applied to items other than files. All of the algorithms implemented below assume that "optimal" means "use up as much of the disc's capacity as possible", but each produces slightly different results. For instance, the best fit and first fit algorithms tend to include fewer files than the worst fit and alternate fit algorithms, even if they use the disc space more efficiently. Usually, for a given set of circumstances, it will be obvious to a human which algorithm is the right one to use, based on trade-offs between number of files included and ideal space utilization. It's a little more difficult to do this programmatically. For Cedar Backup's purposes (i.e. trying to fit a small number of collect-directory tarfiles onto a disc), worst-fit is probably the best choice if the goal is to include as many of the collect directories as possible. :author: Kenneth J. Pronovici """ ####################################################################### # Public functions ####################################################################### ###################### # firstFit() function ###################### def firstFit(items, capacity): """ Implements the first-fit knapsack algorithm. The first-fit algorithm proceeds through an unsorted list of items until running out of items or meeting capacity exactly. If capacity is exceeded, the item that caused capacity to be exceeded is thrown away and the next one is tried. This algorithm generally performs more poorly than the other algorithms both in terms of capacity utilization and item utilization, but can be as much as an order of magnitude faster on large lists of items because it doesn't require any sorting. The "size" values in the items and capacity arguments must be comparable, but they are unitless from the perspective of this function. Zero-sized items and capacity are considered degenerate cases. If capacity is zero, no items fit, period, even if the items list contains zero-sized items. The dictionary is indexed by its key, and then includes its key. This seems kind of strange on first glance. It works this way to facilitate easy sorting of the list on key if needed. The function assumes that the list of items may be used destructively, if needed. This avoids the overhead of having the function make a copy of the list, if this is not required. Callers should pass ``items.copy()`` if they do not want their version of the list modified. The function returns a list of chosen items and the unitless amount of capacity used by the items. Args: items (dictionary, keyed on item, of ``item, size`` tuples, item as string and size as integer): Items to operate on capacity (integer): Capacity of container to fit to Returns: Tuple ``(items, used)`` as described above """ # Use dict since insert into dict is faster than list append included = {} # Search the list as it stands (arbitrary order) used = 0 remaining = capacity for key in list(items.keys()): if remaining == 0: break if remaining - items[key][1] >= 0: included[key] = None used += items[key][1] remaining -= items[key][1] # Return results return (list(included.keys()), used) ##################### # bestFit() function ##################### def bestFit(items, capacity): """ Implements the best-fit knapsack algorithm. The best-fit algorithm proceeds through a sorted list of items (sorted from largest to smallest) until running out of items or meeting capacity exactly. If capacity is exceeded, the item that caused capacity to be exceeded is thrown away and the next one is tried. The algorithm effectively includes the minimum number of items possible in its search for optimal capacity utilization. For large lists of mixed-size items, it's not ususual to see the algorithm achieve 100% capacity utilization by including fewer than 1% of the items. Probably because it often has to look at fewer of the items before completing, it tends to be a little faster than the worst-fit or alternate-fit algorithms. The "size" values in the items and capacity arguments must be comparable, but they are unitless from the perspective of this function. Zero-sized items and capacity are considered degenerate cases. If capacity is zero, no items fit, period, even if the items list contains zero-sized items. The dictionary is indexed by its key, and then includes its key. This seems kind of strange on first glance. It works this way to facilitate easy sorting of the list on key if needed. The function assumes that the list of items may be used destructively, if needed. This avoids the overhead of having the function make a copy of the list, if this is not required. Callers should pass ``items.copy()`` if they do not want their version of the list modified. The function returns a list of chosen items and the unitless amount of capacity used by the items. Args: items (dictionary, keyed on item, of ``item, size`` tuples, item as string and size as integer): Items to operate on capacity (integer): Capacity of container to fit to Returns: Tuple ``(items, used)`` as described above """ # Use dict since insert into dict is faster than list append included = {} # Sort the list from largest to smallest itemlist = list(items.items()) itemlist.sort(key=lambda x: x[1][1], reverse=True) # sort descending keys = [] for item in itemlist: keys.append(item[0]) # Search the list used = 0 remaining = capacity for key in keys: if remaining == 0: break if remaining - items[key][1] >= 0: included[key] = None used += items[key][1] remaining -= items[key][1] # Return the results return (list(included.keys()), used) ###################### # worstFit() function ###################### def worstFit(items, capacity): """ Implements the worst-fit knapsack algorithm. The worst-fit algorithm proceeds through an a sorted list of items (sorted from smallest to largest) until running out of items or meeting capacity exactly. If capacity is exceeded, the item that caused capacity to be exceeded is thrown away and the next one is tried. The algorithm effectively includes the maximum number of items possible in its search for optimal capacity utilization. It tends to be somewhat slower than either the best-fit or alternate-fit algorithm, probably because on average it has to look at more items before completing. The "size" values in the items and capacity arguments must be comparable, but they are unitless from the perspective of this function. Zero-sized items and capacity are considered degenerate cases. If capacity is zero, no items fit, period, even if the items list contains zero-sized items. The dictionary is indexed by its key, and then includes its key. This seems kind of strange on first glance. It works this way to facilitate easy sorting of the list on key if needed. The function assumes that the list of items may be used destructively, if needed. This avoids the overhead of having the function make a copy of the list, if this is not required. Callers should pass ``items.copy()`` if they do not want their version of the list modified. The function returns a list of chosen items and the unitless amount of capacity used by the items. Args: items (dictionary, keyed on item, of ``item, size`` tuples, item as string and size as integer): Items to operate on capacity (integer): Capacity of container to fit to Returns: Tuple ``(items, used)`` as described above """ # Use dict since insert into dict is faster than list append included = {} # Sort the list from smallest to largest itemlist = list(items.items()) itemlist.sort(key=lambda x: x[1][1]) # sort ascending keys = [] for item in itemlist: keys.append(item[0]) # Search the list used = 0 remaining = capacity for key in keys: if remaining == 0: break if remaining - items[key][1] >= 0: included[key] = None used += items[key][1] remaining -= items[key][1] # Return results return (list(included.keys()), used) ########################## # alternateFit() function ########################## def alternateFit(items, capacity): """ Implements the alternate-fit knapsack algorithm. This algorithm (which I'm calling "alternate-fit" as in "alternate from one to the other") tries to balance small and large items to achieve better end-of-disk performance. Instead of just working one direction through a list, it alternately works from the start and end of a sorted list (sorted from smallest to largest), throwing away any item which causes capacity to be exceeded. The algorithm tends to be slower than the best-fit and first-fit algorithms, and slightly faster than the worst-fit algorithm, probably because of the number of items it considers on average before completing. It often achieves slightly better capacity utilization than the worst-fit algorithm, while including slighly fewer items. The "size" values in the items and capacity arguments must be comparable, but they are unitless from the perspective of this function. Zero-sized items and capacity are considered degenerate cases. If capacity is zero, no items fit, period, even if the items list contains zero-sized items. The dictionary is indexed by its key, and then includes its key. This seems kind of strange on first glance. It works this way to facilitate easy sorting of the list on key if needed. The function assumes that the list of items may be used destructively, if needed. This avoids the overhead of having the function make a copy of the list, if this is not required. Callers should pass ``items.copy()`` if they do not want their version of the list modified. The function returns a list of chosen items and the unitless amount of capacity used by the items. Args: items (dictionary, keyed on item, of ``item, size`` tuples, item as string and size as integer): Items to operate on capacity (integer): Capacity of container to fit to Returns: Tuple ``(items, used)`` as described above """ # Use dict since insert into dict is faster than list append included = {} # Sort the list from smallest to largest itemlist = list(items.items()) itemlist.sort(key=lambda x: x[1][1]) # sort ascending keys = [] for item in itemlist: keys.append(item[0]) # Search the list used = 0 remaining = capacity front = keys[0 : len(keys) // 2] back = keys[len(keys) // 2 : len(keys)] back.reverse() i = 0 j = 0 while remaining > 0 and (i < len(front) or j < len(back)): if i < len(front): if remaining - items[front[i]][1] >= 0: included[front[i]] = None used += items[front[i]][1] remaining -= items[front[i]][1] i += 1 if j < len(back): if remaining - items[back[j]][1] >= 0: included[back[j]] = None used += items[back[j]][1] remaining -= items[back[j]][1] j += 1 # Return results return (list(included.keys()), used) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/peer.py0000644000000000000000000014565614567004737015520 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides backup peer-related objects. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides backup peer-related objects and utility functions. Module Attributes ================= Attributes: DEF_COLLECT_INDICATOR: Name of the default collect indicator file DEF_STAGE_INDICATOR: Name of the default stage indicator file :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import shutil import sys from CedarBackup3.config import VALID_FAILURE_MODES from CedarBackup3.filesystem import FilesystemList from CedarBackup3.util import encodePath, executeCommand, isRunningAsRoot, pathJoin, resolveCommand, splitCommandLine ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.peer") DEF_RCP_COMMAND = ["/usr/bin/scp", "-B", "-q", "-C"] DEF_RSH_COMMAND = ["/usr/bin/ssh"] DEF_CBACK_COMMAND = "/usr/bin/cback3" DEF_COLLECT_INDICATOR = "cback.collect" DEF_STAGE_INDICATOR = "cback.stage" SU_COMMAND = ["su"] ######################################################################## # LocalPeer class definition ######################################################################## class LocalPeer(object): ###################### # Class documentation ###################### """ Backup peer representing a local peer in a backup pool. This is a class representing a local (non-network) peer in a backup pool. Local peers are backed up by simple filesystem copy operations. A local peer has associated with it a name (typically, but not necessarily, a hostname) and a collect directory. The public methods other than the constructor are part of a "backup peer" interface shared with the ``RemotePeer`` class. """ ############## # Constructor ############## def __init__(self, name, collectDir, ignoreFailureMode=None): """ Initializes a local backup peer. Note that the collect directory must be an absolute path, but does not have to exist when the object is instantiated. We do a lazy validation on this value since we could (potentially) be creating peer objects before an ongoing backup completed. Args: name: Name of the backup peer collectDir: Path to the peer's collect directory ignoreFailureMode: Ignore failure mode for this peer, one of ``VALID_FAILURE_MODES`` Raises: ValueError: If the name is empty ValueError: If collect directory is not an absolute path """ self._name = None self._collectDir = None self._ignoreFailureMode = None self.name = name self.collectDir = collectDir self.ignoreFailureMode = ignoreFailureMode ############# # Properties ############# def _setName(self, value): """ Property target used to set the peer name. The value must be a non-empty string and cannot be ``None``. Raises: ValueError: If the value is an empty string or ``None`` """ if value is None or len(value) < 1: raise ValueError("Peer name must be a non-empty string.") self._name = value def _getName(self): """ Property target used to get the peer name. """ return self._name def _setCollectDir(self, value): """ Property target used to set the collect directory. The value must be an absolute path and cannot be ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is ``None`` or is not an absolute path ValueError: If a path cannot be encoded properly """ if value is None or not os.path.isabs(value): raise ValueError("Collect directory must be an absolute path.") self._collectDir = encodePath(value) def _getCollectDir(self): """ Property target used to get the collect directory. """ return self._collectDir def _setIgnoreFailureMode(self, value): """ Property target used to set the ignoreFailure mode. If not ``None``, the mode must be one of the values in :any:`VALID_FAILURE_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_FAILURE_MODES: raise ValueError("Ignore failure mode must be one of %s." % VALID_FAILURE_MODES) self._ignoreFailureMode = value def _getIgnoreFailureMode(self): """ Property target used to get the ignoreFailure mode. """ return self._ignoreFailureMode name = property(_getName, _setName, None, "Name of the peer.") collectDir = property(_getCollectDir, _setCollectDir, None, "Path to the peer's collect directory (an absolute local path).") ignoreFailureMode = property(_getIgnoreFailureMode, _setIgnoreFailureMode, None, "Ignore failure mode for peer.") ################# # Public methods ################# def stagePeer(self, targetDir, ownership=None, permissions=None): """ Stages data from the peer into the indicated local target directory. The collect and target directories must both already exist before this method is called. If passed in, ownership and permissions will be applied to the files that are copied. *Note:* The caller is responsible for checking that the indicator exists, if they care. This function only stages the files within the directory. *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. Args: targetDir: Target directory to write data into ownership: Owner and group that files should have, tuple of numeric ``(uid, gid)`` permissions: Unix permissions mode that the staged files should have, in octal like ``0640`` Returns: Number of files copied from the source directory to the target directory Raises: ValueError: If collect directory is not a directory or does not exist ValueError: If target directory is not a directory, does not exist or is not absolute ValueError: If a path cannot be encoded properly IOError: If there were no files to stage (i.e. the directory was empty) IOError: If there is an IO error copying a file OSError: If there is an OS error copying or changing permissions on a file """ targetDir = encodePath(targetDir) if not os.path.isabs(targetDir): logger.debug("Target directory [%s] not an absolute path.", targetDir) raise ValueError("Target directory must be an absolute path.") if not os.path.exists(self.collectDir) or not os.path.isdir(self.collectDir): logger.debug("Collect directory [%s] is not a directory or does not exist on disk.", self.collectDir) raise ValueError("Collect directory is not a directory or does not exist on disk.") if not os.path.exists(targetDir) or not os.path.isdir(targetDir): logger.debug("Target directory [%s] is not a directory or does not exist on disk.", targetDir) raise ValueError("Target directory is not a directory or does not exist on disk.") count = LocalPeer._copyLocalDir(self.collectDir, targetDir, ownership, permissions) if count == 0: raise IOError("Did not copy any files from local peer.") return count def checkCollectIndicator(self, collectIndicator=None): """ Checks the collect indicator in the peer's staging directory. When a peer has completed collecting its backup files, it will write an empty indicator file into its collect directory. This method checks to see whether that indicator has been written. We're "stupid" here - if the collect directory doesn't exist, you'll naturally get back ``False``. If you need to, you can override the name of the collect indicator file by passing in a different name. Args: collectIndicator: Name of the collect indicator file to check Returns: Boolean true/false depending on whether the indicator exists Raises: ValueError: If a path cannot be encoded properly """ collectIndicator = encodePath(collectIndicator) if collectIndicator is None: return os.path.exists(pathJoin(self.collectDir, DEF_COLLECT_INDICATOR)) else: return os.path.exists(pathJoin(self.collectDir, collectIndicator)) def writeStageIndicator(self, stageIndicator=None, ownership=None, permissions=None): """ Writes the stage indicator in the peer's staging directory. When the master has completed collecting its backup files, it will write an empty indicator file into the peer's collect directory. The presence of this file implies that the staging process is complete. If you need to, you can override the name of the stage indicator file by passing in a different name. *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. Args: stageIndicator: Name of the indicator file to write ownership: Owner and group that files should have, tuple of numeric ``(uid, gid)`` permissions: Unix permissions mode that the staged files should have, in octal like ``0640`` Raises: ValueError: If collect directory is not a directory or does not exist ValueError: If a path cannot be encoded properly IOError: If there is an IO error creating the file OSError: If there is an OS error creating or changing permissions on the file """ stageIndicator = encodePath(stageIndicator) if not os.path.exists(self.collectDir) or not os.path.isdir(self.collectDir): logger.debug("Collect directory [%s] is not a directory or does not exist on disk.", self.collectDir) raise ValueError("Collect directory is not a directory or does not exist on disk.") if stageIndicator is None: fileName = pathJoin(self.collectDir, DEF_STAGE_INDICATOR) else: fileName = pathJoin(self.collectDir, stageIndicator) LocalPeer._copyLocalFile(None, fileName, ownership, permissions) # None for sourceFile results in an empty target ################## # Private methods ################## @staticmethod def _copyLocalDir(sourceDir, targetDir, ownership=None, permissions=None): """ Copies files from the source directory to the target directory. This function is not recursive. Only the files in the directory will be copied. Ownership and permissions will be left at their default values if new values are not specified. The source and target directories are allowed to be soft links to a directory, but besides that soft links are ignored. *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. Args: sourceDir: Source directory targetDir: Target directory ownership: Owner and group that files should have, tuple of numeric ``(uid, gid)`` permissions: Unix permissions mode that the staged files should have, in octal like ``0640`` Returns: Number of files copied from the source directory to the target directory Raises: ValueError: If source or target is not a directory or does not exist ValueError: If a path cannot be encoded properly IOError: If there is an IO error copying the files OSError: If there is an OS error copying or changing permissions on a files """ filesCopied = 0 sourceDir = encodePath(sourceDir) targetDir = encodePath(targetDir) for fileName in os.listdir(sourceDir): sourceFile = pathJoin(sourceDir, fileName) targetFile = pathJoin(targetDir, fileName) LocalPeer._copyLocalFile(sourceFile, targetFile, ownership, permissions) filesCopied += 1 return filesCopied @staticmethod def _copyLocalFile(sourceFile=None, targetFile=None, ownership=None, permissions=None, overwrite=True): """ Copies a source file to a target file. If the source file is ``None`` then the target file will be created or overwritten as an empty file. If the target file is ``None``, this method is a no-op. Attempting to copy a soft link or a directory will result in an exception. *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. *Note:* We will not overwrite a target file that exists when this method is invoked. If the target already exists, we'll raise an exception. Args: sourceFile: Source file to copy targetFile: Target file to create ownership: Owner and group that files should have, tuple of numeric ``(uid, gid)`` permissions: Unix permissions mode that the staged files should have, in octal like ``0640`` overwrite: Indicates whether it's OK to overwrite the target file Raises: ValueError: If the passed-in source file is not a regular file ValueError: If a path cannot be encoded properly IOError: If the target file already exists IOError: If there is an IO error copying the file OSError: If there is an OS error copying or changing permissions on a file """ targetFile = encodePath(targetFile) sourceFile = encodePath(sourceFile) if targetFile is None: return if not overwrite: if os.path.exists(targetFile): raise IOError("Target file [%s] already exists." % targetFile) if sourceFile is None: with open(targetFile, "w") as f: # pylint: disable=unspecified-encoding f.write("") else: if os.path.isfile(sourceFile) and not os.path.islink(sourceFile): shutil.copy(sourceFile, targetFile) else: logger.debug("Source [%s] is not a regular file.", sourceFile) raise ValueError("Source is not a regular file.") if ownership is not None: if sys.platform != "win32": os.chown(targetFile, ownership[0], ownership[1]) # pylint: disable=no-member if permissions is not None: os.chmod(targetFile, permissions) ######################################################################## # RemotePeer class definition ######################################################################## class RemotePeer(object): ###################### # Class documentation ###################### """ Backup peer representing a remote peer in a backup pool. This is a class representing a remote (networked) peer in a backup pool. Remote peers are backed up using an rcp-compatible copy command. A remote peer has associated with it a name (which must be a valid hostname), a collect directory, a working directory and a copy method (an rcp-compatible command). You can also set an optional local user value. This username will be used as the local user for any remote copies that are required. It can only be used if the root user is executing the backup. The root user will ``su`` to the local user and execute the remote copies as that user. The copy method is associated with the peer and not with the actual request to copy, because we can envision that each remote host might have a different connect method. The public methods other than the constructor are part of a "backup peer" interface shared with the ``LocalPeer`` class. """ ############## # Constructor ############## def __init__( self, name=None, collectDir=None, workingDir=None, remoteUser=None, rcpCommand=None, localUser=None, rshCommand=None, cbackCommand=None, ignoreFailureMode=None, ): """ Initializes a remote backup peer. *Note:* If provided, each command will eventually be parsed into a list of strings suitable for passing to ``util.executeCommand`` in order to avoid security holes related to shell interpolation. This parsing will be done by the :any:`util.splitCommandLine` function. See the documentation for that function for some important notes about its limitations. Args: name: Name of the backup peer, a valid DNS name collectDir: Path to the peer's collect directory, absolute path workingDir: Working directory that can be used to create temporary files, etc, an absolute path remoteUser: Name of the Cedar Backup user on the remote peer localUser: Name of the Cedar Backup user on the current host rcpCommand: An rcp-compatible copy command to use for copying files from the peer rshCommand: An rsh-compatible copy command to use for remote shells to the peer cbackCommand: A chack-compatible command to use for executing managed actions ignoreFailureMode: Ignore failure mode for this peer, one of ``VALID_FAILURE_MODES`` Raises: ValueError: If collect directory is not an absolute path """ self._name = None self._collectDir = None self._workingDir = None self._remoteUser = None self._localUser = None self._rcpCommand = None self._rcpCommandList = None self._rshCommand = None self._rshCommandList = None self._cbackCommand = None self._ignoreFailureMode = None self.name = name self.collectDir = collectDir self.workingDir = workingDir self.remoteUser = remoteUser self.localUser = localUser self.rcpCommand = rcpCommand self.rshCommand = rshCommand self.cbackCommand = cbackCommand self.ignoreFailureMode = ignoreFailureMode ############# # Properties ############# def _setName(self, value): """ Property target used to set the peer name. The value must be a non-empty string and cannot be ``None``. Raises: ValueError: If the value is an empty string or ``None`` """ if value is None or len(value) < 1: raise ValueError("Peer name must be a non-empty string.") self._name = value def _getName(self): """ Property target used to get the peer name. """ return self._name def _setCollectDir(self, value): """ Property target used to set the collect directory. The value must be an absolute path and cannot be ``None``. It does not have to exist on disk at the time of assignment. Raises: ValueError: If the value is ``None`` or is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Collect directory must be an absolute path.") self._collectDir = encodePath(value) def _getCollectDir(self): """ Property target used to get the collect directory. """ return self._collectDir def _setWorkingDir(self, value): """ Property target used to set the working directory. The value must be an absolute path and cannot be ``None``. Raises: ValueError: If the value is ``None`` or is not an absolute path ValueError: If the value cannot be encoded properly """ if value is not None: if not os.path.isabs(value): raise ValueError("Working directory must be an absolute path.") self._workingDir = encodePath(value) def _getWorkingDir(self): """ Property target used to get the working directory. """ return self._workingDir def _setRemoteUser(self, value): """ Property target used to set the remote user. The value must be a non-empty string and cannot be ``None``. Raises: ValueError: If the value is an empty string or ``None`` """ if value is None or len(value) < 1: raise ValueError("Peer remote user must be a non-empty string.") self._remoteUser = value def _getRemoteUser(self): """ Property target used to get the remote user. """ return self._remoteUser def _setLocalUser(self, value): """ Property target used to set the local user. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("Peer local user must be a non-empty string.") self._localUser = value def _getLocalUser(self): """ Property target used to get the local user. """ return self._localUser def _setRcpCommand(self, value): """ Property target to set the rcp command. The value must be a non-empty string or ``None``. Its value is stored in the two forms: "raw" as provided by the client, and "parsed" into a list suitable for being passed to :any:`util.executeCommand` via :any:`util.splitCommandLine`. However, all the caller will ever see via the property is the actual value they set (which includes seeing ``None``, even if we translate that internally to ``DEF_RCP_COMMAND``). Internally, we should always use ``self._rcpCommandList`` if we want the actual command list. Raises: ValueError: If the value is an empty string """ if value is None: self._rcpCommand = None self._rcpCommandList = DEF_RCP_COMMAND else: if len(value) >= 1: self._rcpCommand = value self._rcpCommandList = splitCommandLine(self._rcpCommand) else: raise ValueError("The rcp command must be a non-empty string.") def _getRcpCommand(self): """ Property target used to get the rcp command. """ return self._rcpCommand def _setRshCommand(self, value): """ Property target to set the rsh command. The value must be a non-empty string or ``None``. Its value is stored in the two forms: "raw" as provided by the client, and "parsed" into a list suitable for being passed to :any:`util.executeCommand` via :any:`util.splitCommandLine`. However, all the caller will ever see via the property is the actual value they set (which includes seeing ``None``, even if we translate that internally to ``DEF_RSH_COMMAND``). Internally, we should always use ``self._rshCommandList`` if we want the actual command list. Raises: ValueError: If the value is an empty string """ if value is None: self._rshCommand = None self._rshCommandList = DEF_RSH_COMMAND else: if len(value) >= 1: self._rshCommand = value self._rshCommandList = splitCommandLine(self._rshCommand) else: raise ValueError("The rsh command must be a non-empty string.") def _getRshCommand(self): """ Property target used to get the rsh command. """ return self._rshCommand def _setCbackCommand(self, value): """ Property target to set the cback command. The value must be a non-empty string or ``None``. Unlike the other command, this value is only stored in the "raw" form provided by the client. Raises: ValueError: If the value is an empty string """ if value is None: self._cbackCommand = None else: if len(value) >= 1: self._cbackCommand = value else: raise ValueError("The cback command must be a non-empty string.") def _getCbackCommand(self): """ Property target used to get the cback command. """ return self._cbackCommand def _setIgnoreFailureMode(self, value): """ Property target used to set the ignoreFailure mode. If not ``None``, the mode must be one of the values in :any:`VALID_FAILURE_MODES`. Raises: ValueError: If the value is not valid """ if value is not None: if value not in VALID_FAILURE_MODES: raise ValueError("Ignore failure mode must be one of %s." % VALID_FAILURE_MODES) self._ignoreFailureMode = value def _getIgnoreFailureMode(self): """ Property target used to get the ignoreFailure mode. """ return self._ignoreFailureMode name = property(_getName, _setName, None, "Name of the peer (a valid DNS hostname).") collectDir = property(_getCollectDir, _setCollectDir, None, "Path to the peer's collect directory (an absolute local path).") workingDir = property(_getWorkingDir, _setWorkingDir, None, "Path to the peer's working directory (an absolute local path).") remoteUser = property(_getRemoteUser, _setRemoteUser, None, "Name of the Cedar Backup user on the remote peer.") localUser = property(_getLocalUser, _setLocalUser, None, "Name of the Cedar Backup user on the current host.") rcpCommand = property(_getRcpCommand, _setRcpCommand, None, "An rcp-compatible copy command to use for copying files.") rshCommand = property(_getRshCommand, _setRshCommand, None, "An rsh-compatible command to use for remote shells to the peer.") cbackCommand = property( _getCbackCommand, _setCbackCommand, None, "A chack-compatible command to use for executing managed actions." ) ignoreFailureMode = property(_getIgnoreFailureMode, _setIgnoreFailureMode, None, "Ignore failure mode for peer.") ################# # Public methods ################# def stagePeer(self, targetDir, ownership=None, permissions=None): """ Stages data from the peer into the indicated local target directory. The target directory must already exist before this method is called. If passed in, ownership and permissions will be applied to the files that are copied. *Note:* The returned count of copied files might be inaccurate if some of the copied files already existed in the staging directory prior to the copy taking place. We don't clear the staging directory first, because some extension might also be using it. *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. *Note:* Unlike the local peer version of this method, an I/O error might or might not be raised if the directory is empty. Since we're using a remote copy method, we just don't have the fine-grained control over our exceptions that's available when we can look directly at the filesystem, and we can't control whether the remote copy method thinks an empty directory is an error. Args: targetDir: Target directory to write data into ownership: Owner and group that files should have, tuple of numeric ``(uid, gid)`` permissions: Unix permissions mode that the staged files should have, in octal like ``0640`` Returns: Number of files copied from the source directory to the target directory Raises: ValueError: If target directory is not a directory, does not exist or is not absolute ValueError: If a path cannot be encoded properly IOError: If there were no files to stage (i.e. the directory was empty) IOError: If there is an IO error copying a file OSError: If there is an OS error copying or changing permissions on a file """ targetDir = encodePath(targetDir) if not os.path.isabs(targetDir): logger.debug("Target directory [%s] not an absolute path.", targetDir) raise ValueError("Target directory must be an absolute path.") if not os.path.exists(targetDir) or not os.path.isdir(targetDir): logger.debug("Target directory [%s] is not a directory or does not exist on disk.", targetDir) raise ValueError("Target directory is not a directory or does not exist on disk.") count = RemotePeer._copyRemoteDir( self.remoteUser, self.localUser, self.name, self._rcpCommand, self._rcpCommandList, self.collectDir, targetDir, ownership, permissions, ) if count == 0: raise IOError("Did not copy any files from local peer.") return count def checkCollectIndicator(self, collectIndicator=None): """ Checks the collect indicator in the peer's staging directory. When a peer has completed collecting its backup files, it will write an empty indicator file into its collect directory. This method checks to see whether that indicator has been written. If the remote copy command fails, we return ``False`` as if the file weren't there. If you need to, you can override the name of the collect indicator file by passing in a different name. *Note:* Apparently, we can't count on all rcp-compatible implementations to return sensible errors for some error conditions. As an example, the ``scp`` command in Debian 'woody' returns a zero (normal) status even when it can't find a host or if the login or path is invalid. Because of this, the implementation of this method is rather convoluted. Args: collectIndicator: Name of the collect indicator file to check Returns: Boolean true/false depending on whether the indicator exists Raises: ValueError: If a path cannot be encoded properly """ try: if collectIndicator is None: sourceFile = pathJoin(self.collectDir, DEF_COLLECT_INDICATOR) targetFile = pathJoin(self.workingDir, DEF_COLLECT_INDICATOR) else: collectIndicator = encodePath(collectIndicator) sourceFile = pathJoin(self.collectDir, collectIndicator) targetFile = pathJoin(self.workingDir, collectIndicator) logger.debug("Fetch remote [%s] into [%s].", sourceFile, targetFile) if os.path.exists(targetFile): try: os.remove(targetFile) except: raise Exception("Error: collect indicator [%s] already exists!" % targetFile) try: RemotePeer._copyRemoteFile( self.remoteUser, self.localUser, self.name, self._rcpCommand, self._rcpCommandList, sourceFile, targetFile, overwrite=False, ) if os.path.exists(targetFile): return True else: return False except Exception as e: logger.info("Failed looking for collect indicator: %s", e) return False finally: if os.path.exists(targetFile): try: os.remove(targetFile) except: pass def writeStageIndicator(self, stageIndicator=None): """ Writes the stage indicator in the peer's staging directory. When the master has completed collecting its backup files, it will write an empty indicator file into the peer's collect directory. The presence of this file implies that the staging process is complete. If you need to, you can override the name of the stage indicator file by passing in a different name. *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. Args: stageIndicator: Name of the indicator file to write Raises: ValueError: If a path cannot be encoded properly IOError: If there is an IO error creating the file OSError: If there is an OS error creating or changing permissions on the file """ stageIndicator = encodePath(stageIndicator) if stageIndicator is None: sourceFile = pathJoin(self.workingDir, DEF_STAGE_INDICATOR) targetFile = pathJoin(self.collectDir, DEF_STAGE_INDICATOR) else: sourceFile = pathJoin(self.workingDir, DEF_STAGE_INDICATOR) targetFile = pathJoin(self.collectDir, stageIndicator) try: if not os.path.exists(sourceFile): with open(sourceFile, "w") as f: # pylint: disable=unspecified-encoding f.write("") RemotePeer._pushLocalFile( self.remoteUser, self.localUser, self.name, self._rcpCommand, self._rcpCommandList, sourceFile, targetFile ) finally: if os.path.exists(sourceFile): try: os.remove(sourceFile) except: pass def executeRemoteCommand(self, command): """ Executes a command on the peer via remote shell. Args: command: Command to execute Raises: IOError: If there is an error executing the command on the remote peer """ RemotePeer._executeRemoteCommand( self.remoteUser, self.localUser, self.name, self._rshCommand, self._rshCommandList, command ) def executeManagedAction(self, action, fullBackup): """ Executes a managed action on this peer. Args: action: Name of the action to execute fullBackup: Whether a full backup should be executed Raises: IOError: If there is an error executing the action on the remote peer """ try: command = RemotePeer._buildCbackCommand(self.cbackCommand, action, fullBackup) self.executeRemoteCommand(command) except IOError as e: logger.info(e) raise IOError("Failed to execute action [%s] on managed client [%s]." % (action, self.name)) ################## # Private methods ################## @staticmethod def _getDirContents(path): """ Returns the contents of a directory in terms of a Set. The directory's contents are read as a :any:`FilesystemList` containing only files, and then the list is converted into a set object for later use. Args: path: Directory path to get contents for Returns: Set of files in the directory Raises: ValueError: If path is not a directory or does not exist """ contents = FilesystemList() contents.excludeDirs = True contents.excludeLinks = True contents.addDirContents(path) return set(contents) @staticmethod def _copyRemoteDir( remoteUser, localUser, remoteHost, rcpCommand, rcpCommandList, sourceDir, targetDir, ownership=None, permissions=None ): """ Copies files from the source directory to the target directory. This function is not recursive. Only the files in the directory will be copied. Ownership and permissions will be left at their default values if new values are not specified. Behavior when copying soft links from the collect directory is dependent on the behavior of the specified rcp command. *Note:* The returned count of copied files might be inaccurate if some of the copied files already existed in the staging directory prior to the copy taking place. We don't clear the staging directory first, because some extension might also be using it. *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. *Note:* We don't have a good way of knowing exactly what files we copied down from the remote peer, unless we want to parse the output of the rcp command (ugh). We could change permissions on everything in the target directory, but that's kind of ugly too. Instead, we use Python's set functionality to figure out what files were added while we executed the rcp command. This isn't perfect - for instance, it's not correct if someone else is messing with the directory at the same time we're doing the remote copy - but it's about as good as we're going to get. *Note:* Apparently, we can't count on all rcp-compatible implementations to return sensible errors for some error conditions. As an example, the ``scp`` command in Debian 'woody' returns a zero (normal) status even when it can't find a host or if the login or path is invalid. We try to work around this by issuing ``IOError`` if we don't copy any files from the remote host. Args: remoteUser: Name of the Cedar Backup user on the remote peer localUser: Name of the Cedar Backup user on the current host remoteHost: Hostname of the remote peer rcpCommand: An rcp-compatible copy command to use for copying files from the peer rcpCommandList: An rcp-compatible copy command to use for copying files, as for :any:`util.executeCommand` sourceDir: Source directory targetDir: Target directory ownership: Owner and group that files should have, tuple of numeric ``(uid, gid)`` permissions: Unix permissions mode that the staged files should have, in octal like ``0640`` Returns: Number of files copied from the source directory to the target directory Raises: ValueError: If source or target is not a directory or does not exist IOError: If there is an IO error copying the files """ beforeSet = RemotePeer._getDirContents(targetDir) if localUser is not None: try: if not isRunningAsRoot(): raise IOError("Only root can remote copy as another user.") except AttributeError: pass actualCommand = "%s %s@%s:%s/* %s" % (rcpCommand, remoteUser, remoteHost, sourceDir, targetDir) command = resolveCommand(SU_COMMAND) result = executeCommand(command, [localUser, "-c", actualCommand])[0] if result != 0: raise IOError("Error (%d) copying files from remote host as local user [%s]." % (result, localUser)) else: copySource = "%s@%s:%s/*" % (remoteUser, remoteHost, sourceDir) command = resolveCommand(rcpCommandList) result = executeCommand(command, [copySource, targetDir])[0] if result != 0: raise IOError("Error (%d) copying files from remote host." % result) afterSet = RemotePeer._getDirContents(targetDir) if len(afterSet) == 0: raise IOError("Did not copy any files from remote peer.") differenceSet = afterSet.difference(beforeSet) # files we added as part of copy if len(differenceSet) == 0: raise IOError("Apparently did not copy any new files from remote peer.") for targetFile in differenceSet: if ownership is not None: if sys.platform != "win32": os.chown(targetFile, ownership[0], ownership[1]) # pylint: disable=no-member if permissions is not None: os.chmod(targetFile, permissions) return len(differenceSet) @staticmethod def _copyRemoteFile( remoteUser, localUser, remoteHost, rcpCommand, rcpCommandList, sourceFile, targetFile, ownership=None, permissions=None, overwrite=True, ): """ Copies a remote source file to a target file. *Note:* Internally, we have to go through and escape any spaces in the source path with double-backslash, otherwise things get screwed up. It doesn't seem to be required in the target path. I hope this is portable to various different rcp methods, but I guess it might not be (all I have to test with is OpenSSH). *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. *Note:* We will not overwrite a target file that exists when this method is invoked. If the target already exists, we'll raise an exception. *Note:* Apparently, we can't count on all rcp-compatible implementations to return sensible errors for some error conditions. As an example, the ``scp`` command in Debian 'woody' returns a zero (normal) status even when it can't find a host or if the login or path is invalid. We try to work around this by issuing ``IOError`` the target file does not exist when we're done. Args: remoteUser: Name of the Cedar Backup user on the remote peer remoteHost: Hostname of the remote peer localUser: Name of the Cedar Backup user on the current host rcpCommand: An rcp-compatible copy command to use for copying files from the peer rcpCommandList: An rcp-compatible copy command to use for copying files, as for :any:`util.executeCommand` sourceFile: Source file to copy targetFile: Target file to create ownership: Owner and group that files should have, tuple of numeric ``(uid, gid)`` permissions: Unix permissions mode that the staged files should have, in octal like ``0640`` overwrite: Indicates whether it's OK to overwrite the target file Raises: IOError: If the target file already exists IOError: If there is an IO error copying the file OSError: If there is an OS error changing permissions on the file """ if not overwrite: if os.path.exists(targetFile): raise IOError("Target file [%s] already exists." % targetFile) if localUser is not None: try: if not isRunningAsRoot(): raise IOError("Only root can remote copy as another user.") except AttributeError: pass actualCommand = "%s %s@%s:%s %s" % (rcpCommand, remoteUser, remoteHost, sourceFile.replace(" ", "\\ "), targetFile) command = resolveCommand(SU_COMMAND) result = executeCommand(command, [localUser, "-c", actualCommand])[0] if result != 0: raise IOError("Error (%d) copying [%s] from remote host as local user [%s]." % (result, sourceFile, localUser)) else: copySource = "%s@%s:%s" % (remoteUser, remoteHost, sourceFile.replace(" ", "\\ ")) command = resolveCommand(rcpCommandList) result = executeCommand(command, [copySource, targetFile])[0] if result != 0: raise IOError("Error (%d) copying [%s] from remote host." % (result, sourceFile)) if not os.path.exists(targetFile): raise IOError("Apparently unable to copy file from remote host.") if ownership is not None: if sys.platform != "win32": os.chown(targetFile, ownership[0], ownership[1]) # pylint: disable=no-member if permissions is not None: os.chmod(targetFile, permissions) @staticmethod def _pushLocalFile(remoteUser, localUser, remoteHost, rcpCommand, rcpCommandList, sourceFile, targetFile, overwrite=True): """ Copies a local source file to a remote host. *Note:* We will not overwrite a target file that exists when this method is invoked. If the target already exists, we'll raise an exception. *Note:* Internally, we have to go through and escape any spaces in the source and target paths with double-backslash, otherwise things get screwed up. I hope this is portable to various different rcp methods, but I guess it might not be (all I have to test with is OpenSSH). *Note:* If you have user/group as strings, call the :any:`util.getUidGid` function to get the associated uid/gid as an ownership tuple. Args: remoteUser: Name of the Cedar Backup user on the remote peer localUser: Name of the Cedar Backup user on the current host remoteHost: Hostname of the remote peer rcpCommand: An rcp-compatible copy command to use for copying files from the peer rcpCommandList: An rcp-compatible copy command to use for copying files, as for :any:`util.executeCommand` sourceFile: Source file to copy targetFile: Target file to create overwrite: Indicates whether it's OK to overwrite the target file Raises: IOError: If there is an IO error copying the file OSError: If there is an OS error changing permissions on the file """ if not overwrite: if os.path.exists(targetFile): raise IOError("Target file [%s] already exists." % targetFile) if localUser is not None: try: if not isRunningAsRoot(): raise IOError("Only root can remote copy as another user.") except AttributeError: pass actualCommand = '%s "%s" "%s@%s:%s"' % (rcpCommand, sourceFile, remoteUser, remoteHost, targetFile) command = resolveCommand(SU_COMMAND) result = executeCommand(command, [localUser, "-c", actualCommand])[0] if result != 0: raise IOError("Error (%d) copying [%s] to remote host as local user [%s]." % (result, sourceFile, localUser)) else: copyTarget = "%s@%s:%s" % (remoteUser, remoteHost, targetFile.replace(" ", "\\ ")) command = resolveCommand(rcpCommandList) result = executeCommand(command, [sourceFile.replace(" ", "\\ "), copyTarget])[0] if result != 0: raise IOError("Error (%d) copying [%s] to remote host." % (result, sourceFile)) @staticmethod def _executeRemoteCommand(remoteUser, localUser, remoteHost, rshCommand, rshCommandList, remoteCommand): """ Executes a command on the peer via remote shell. Args: remoteUser: Name of the Cedar Backup user on the remote peer localUser: Name of the Cedar Backup user on the current host remoteHost: Hostname of the remote peer rshCommand: An rsh-compatible copy command to use for remote shells to the peer rshCommandList: An rsh-compatible copy command to use for remote shells to the peer, as for :any:`util.executeCommand` remoteCommand: The command to be executed on the remote host, with no special shell characters Raises: IOError: If there is an error executing the remote command """ actualCommand = "%s %s@%s '%s'" % (rshCommand, remoteUser, remoteHost, remoteCommand) if localUser is not None: try: if not isRunningAsRoot(): raise IOError("Only root can remote shell as another user.") except AttributeError: pass command = resolveCommand(SU_COMMAND) result = executeCommand(command, [localUser, "-c", actualCommand])[0] if result != 0: raise IOError('Command failed [su -c %s "%s"]' % (localUser, actualCommand)) else: command = resolveCommand(rshCommandList) result = executeCommand(command, ["%s@%s" % (remoteUser, remoteHost), "%s" % remoteCommand])[0] if result != 0: raise IOError("Command failed [%s]" % (actualCommand)) @staticmethod def _buildCbackCommand(cbackCommand, action, fullBackup): """ Builds a Cedar Backup command line for the named action. *Note:* If the cback command is None, then DEF_CBACK_COMMAND is used. Args: cbackCommand: cback command to execute, including required options action: Name of the action to execute fullBackup: Whether a full backup should be executed Returns: String suitable for passing to :any:`_executeRemoteCommand` as remoteCommand Raises: ValueError: If action is None """ if action is None: raise ValueError("Action cannot be None.") if cbackCommand is None: cbackCommand = DEF_CBACK_COMMAND if fullBackup: return "%s --full %s" % (cbackCommand, action) else: return "%s %s" % (cbackCommand, action) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/release.py0000644000000000000000000000410414567004737016163 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides location to maintain release information. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # """ Provides a location to maintain version information. Module Attributes ================= Attributes: AUTHOR: Author of software EMAIL: Email address of author VERSION: Software version URL: Homepage URL :author: Kenneth J. Pronovici """ # Historically, this information was tracked directly within this file as part of the # release process. In modern Python, it's better to rely on the package metadata, which # is managed by Poetry on our behalf. # # The metadata will always be set any time the package has been completely and properly # installed. However, there are other cases where it won't be available, such as when # running the smoke tests during the Debian build process, or when running the unit tests # from within the source tree as a part of the Debian CI suite. So, default values are # provided. # # Note: previously, we also tracked release date and copyright date range, but that # information is not available in the package metadata. These values are maintained to # avoid breaking the public interface, but are always "unset". from importlib_metadata import metadata try: _METADATA = metadata("cedar-backup3") except ImportError: _METADATA = {} AUTHOR = _METADATA["Author"] if "Author" in _METADATA else "unset" EMAIL = _METADATA["Author-email"] if "Author-email" in _METADATA else "unset" VERSION = _METADATA["Version"] if "Version" in _METADATA else "0.0.0" URL = _METADATA["Home-page"] if "Home-page" in _METADATA else "unset" COPYRIGHT = "unset" DATE = "unset" ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/scripts.py0000644000000000000000000000240114567004737016230 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Scripts published by Poetry as part of the Python package # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # """ Scripts published by Poetry as part of the Python package. """ import sys def cback3(): """Implementation of the cback3 script, the main command-line interface.""" from CedarBackup3.cli import cli # pylint: disable=import-outside-toplevel result = cli() sys.exit(result) def amazons3(): """Implementation of the cback3-amazons3-sync script.""" from CedarBackup3.tools.amazons3 import cli # pylint: disable=import-outside-toplevel result = cli() sys.exit(result) def span(): """Implementation of the cback3-span script.""" from CedarBackup3.tools.span import cli # pylint: disable=import-outside-toplevel result = cli() sys.exit(result) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/testutil.py0000644000000000000000000004013014567004737016417 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2006,2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides unit-testing utilities. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides unit-testing utilities. These utilities are kept here, separate from util.py, because they provide common functionality that I do not want exported "publicly" once Cedar Backup is installed on a system. They are only used for unit testing, and are only useful within the source tree. Many of these functions are in here because they are "good enough" for unit test work but are not robust enough to be real public functions. Others (like :any:`removedir`) do what they are supposed to, but I don't want responsibility for making them available to others. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import getpass import logging import os import platform import random import string # pylint: disable=W0402 import sys import tarfile import time from io import StringIO from CedarBackup3.cli import setupPathResolver from CedarBackup3.config import Config, OptionsConfig from CedarBackup3.customize import customizeOverrides from CedarBackup3.util import encodePath, executeCommand, nullDevice, pathJoin ######################################################################## # Public functions ######################################################################## ############################## # setupDebugLogger() function ############################## def setupDebugLogger(): """ Sets up a screen logger for debugging purposes. Normally, the CLI functionality configures the logger so that things get written to the right place. However, for debugging it's sometimes nice to just get everything -- debug information and output -- dumped to the screen. This function takes care of that. """ logger = logging.getLogger("CedarBackup3") logger.setLevel(logging.DEBUG) # let the logger see all messages formatter = logging.Formatter(fmt="%(message)s") handler = logging.StreamHandler(stream=sys.stdout) handler.setFormatter(formatter) handler.setLevel(logging.DEBUG) logger.addHandler(handler) def configureLogging(): """Optionally disable system logging based on configuration in the environment.""" if "FULL_LOGGING" not in os.environ or os.environ["FULL_LOGGING"] == "N": devnull = nullDevice() handler = logging.FileHandler(filename=devnull) handler.setLevel(logging.NOTSET) logger = logging.getLogger("CedarBackup3") logger.setLevel(logging.NOTSET) logger.addHandler(handler) ################# # setupOverrides ################# def setupOverrides(): """ Set up any platform-specific overrides that might be required. When packages are built, this is done manually (hardcoded) in customize.py and the overrides are set up in cli.cli(). This way, no runtime checks need to be done. This is safe, because the package maintainer knows exactly which platform (Debian or not) the package is being built for. Unit tests are different, because they might be run anywhere. So, we attempt to make a guess about plaform using platformDebian(), and use that to set up the custom overrides so that platform-specific unit tests continue to work. """ config = Config() config.options = OptionsConfig() if platformDebian(): customizeOverrides(config, platform="debian") else: customizeOverrides(config, platform="standard") setupPathResolver(config) ########################### # findResources() function ########################### def findResources(resources, dataDirs): """ Returns a dictionary of locations for various resources. Args: resources: List of required resources dataDirs: List of data directories to search within for resources Returns: Dictionary mapping resource name to resource path Raises: Exception: If some resource cannot be found """ mapping = {} for resource in resources: for resourceDir in dataDirs: path = pathJoin(resourceDir, resource) if os.path.exists(path): mapping[resource] = path break else: raise Exception("Unable to find resource [%s]." % resource) return mapping ############################## # commandAvailable() function ############################## def commandAvailable(command): """ Indicates whether a command is available on $PATH somewhere. This should work on both Windows and UNIX platforms. Args: command: Commang to search for Returns: Boolean true/false depending on whether command is available """ if "PATH" in os.environ: for path in os.environ["PATH"].split(os.sep): if os.path.exists(pathJoin(path, command)): return True return False ####################### # buildPath() function ####################### def buildPath(components): """ Builds a complete path from a list of components. For instance, constructs ``"/a/b/c"`` from ``["/a", "b", "c"]``. Args: components: List of components Returns: String path constructed from components Raises: ValueError: If a path cannot be encoded properly """ path = components[0] for component in components[1:]: path = pathJoin(path, component) return encodePath(path) ####################### # removedir() function ####################### def removedir(tree): """ Recursively removes an entire directory. This is basically taken from an example on python.com. Args: tree: Directory tree to remove Raises: ValueError: If a path cannot be encoded properly """ tree = encodePath(tree) for root, dirs, files in os.walk(tree, topdown=False): for name in files: path = pathJoin(root, name) if os.path.islink(path): os.remove(path) elif os.path.isfile(path): os.remove(path) for name in dirs: path = pathJoin(root, name) if os.path.islink(path): os.remove(path) elif os.path.isdir(path): os.rmdir(path) os.rmdir(tree) ######################## # extractTar() function ######################## def extractTar(tmpdir, filepath): """ Extracts the indicated tar file to the indicated tmpdir. Args: tmpdir: Temp directory to extract to filepath: Path to tarfile to extract Raises: ValueError: If a path cannot be encoded properly """ # pylint: disable=E1101 tmpdir = encodePath(tmpdir) filepath = encodePath(filepath) with tarfile.open(filepath) as tar: try: tar.format = tarfile.GNU_FORMAT except AttributeError: tar.posix = False for tarinfo in tar: # Starting in Python 3.12, we have to explicitly specify filter behavior # See: https://docs.python.org/3/library/tarfile.html#supporting-older-python-versions if hasattr(tarfile, "data_filter"): # noinspection PyArgumentList tar.extract(member=tarinfo, path=tmpdir, filter="data") else: tar.extract(member=tarinfo, path=tmpdir) ########################### # changeFileAge() function ########################### def changeFileAge(filename, subtract=None): """ Changes a file age using the ``os.utime`` function. *Note:* Some platforms don't seem to be able to set an age precisely. As a result, whereas we might have intended to set an age of 86400 seconds, we actually get an age of 86399.375 seconds. When util.calculateFileAge() looks at that the file, it calculates an age of 0.999992766204 days, which then gets truncated down to zero whole days. The tests get very confused. To work around this, I always subtract off one additional second as a fudge factor. That way, the file age will be *at least* as old as requested later on. Args: filename: File to operate on subtract: Number of seconds to subtract from the current time Raises: ValueError: If a path cannot be encoded properly """ filename = encodePath(filename) newTime = time.time() - 1 if subtract is not None: newTime -= subtract os.utime(filename, (newTime, newTime)) ########################### # getMaskAsMode() function ########################### def getMaskAsMode(): """ Returns the user's current umask inverted to a mode. A mode is mostly a bitwise inversion of a mask, i.e. mask 002 is mode 775. Returns: Umask converted to a mode, as an integer """ umask = os.umask(0o777) os.umask(umask) return int(~umask & 0o777) # invert, then use only lower bytes ###################### # getLogin() function ###################### def getLogin(): """ Returns the name of the currently-logged in user. This might fail under some circumstances - but if it does, our tests would fail anyway. """ return getpass.getuser() ############################ # randomFilename() function ############################ # noinspection PyTypeChecker def randomFilename(length, prefix=None, suffix=None): """ Generates a random filename with the given length. @return Random filename """ characters = [None] * length for i in range(length): characters[i] = random.choice(string.ascii_uppercase) if prefix is None: prefix = "" if suffix is None: suffix = "" return "%s%s%s" % (prefix, "".join(characters), suffix) #################################### # failUnlessAssignRaises() function #################################### # pylint: disable=W0613 def failUnlessAssignRaises(testCase, exception, obj, prop, value): """ Equivalent of ``failUnlessRaises``, but used for property assignments instead. It's nice to be able to use ``failUnlessRaises`` to check that a method call raises the exception that you expect. Unfortunately, this method can't be used to check Python propery assignments, even though these property assignments are actually implemented underneath as methods. This function (which can be easily called by unit test classes) provides an easy way to wrap the assignment checks. It's not pretty, or as intuitive as the original check it's modeled on, but it does work. Let's assume you make this method call:: testCase.failUnlessAssignRaises(ValueError, collectDir, "absolutePath", absolutePath) If you do this, a test case failure will be raised unless the assignment:: collectDir.absolutePath = absolutePath fails with a ``ValueError`` exception. The failure message differentiates between the case where no exception was raised and the case where the wrong exception was raised. *Note:* Internally, the ``missed`` and ``instead`` variables are used rather than directly calling ``testCase.fail`` upon noticing a problem because the act of "failure" itself generates an exception that would be caught by the general ``except`` clause. Args: testCase: PyUnit test case object (i.e. self) exception: Exception that is expected to be raised obj: Object whose property is to be assigned to prop: Name of the property, as a string value: Value that is to be assigned to the property @see: ``unittest.TestCase.failUnlessRaises`` """ missed = False instead = None try: exec("obj.%s = value" % prop) # pylint: disable=W0122 missed = True except exception: pass except Exception as e: instead = e if missed: testCase.fail("Expected assignment to raise %s, but got no exception." % (exception.__name__)) if instead is not None: testCase.fail("Expected assignment to raise %s, but got %s instead." % (ValueError, instead.__class__.__name__)) ########################### # captureOutput() function ########################### def captureOutput(c): """ Captures the output (stdout, stderr) of a function or a method. Some of our functions don't do anything other than just print output. We need a way to test these functions (at least nominally) but we don't want any of the output spoiling the test suite output. This function just creates a dummy file descriptor that can be used as a target by the callable function, rather than ``stdout`` or ``stderr``. *Note:* This method assumes that ``callable`` doesn't take any arguments besides keyword argument ``fd`` to specify the file descriptor. Args: c: Callable function or method Returns: Output of function, as one big string """ fd = StringIO() c(fd=fd) result = fd.getvalue() fd.close() return result ######################### # _isPlatform() function ######################### def _isPlatform(name): """ Returns boolean indicating whether we're running on the indicated platform. Args: name: Platform name to check, currently one of "windows" or "macosx" """ if name == "windows": return sys.platform == "win32" elif name == "macosx": return sys.platform == "darwin" elif name == "debian": return platform.platform(False, False).find("debian") > 0 elif name == "cygwin": return platform.platform(True, True).startswith("CYGWIN") else: raise ValueError("Unknown platform [%s]." % name) ############################ # platformDebian() function ############################ def platformDebian(): """ Returns boolean indicating whether this is the Debian platform. """ return _isPlatform("debian") ############################ # platformMacOsX() function ############################ def platformMacOsX(): """ Returns boolean indicating whether this is the Mac OS X platform. """ return _isPlatform("macosx") ############################# # platformWindows() function ############################# def platformWindows(): """ Returns boolean indicating whether this is the Windows platform. """ return _isPlatform("windows") #################################### # platformSupportsLinks() function #################################### def platformSupportsLinks(): """Whether the platform supports soft links""" return not platformWindows() ############################## # availableLocales() function ############################## def availableLocales(): """ Returns a list of available locales on the system Returns: List of string locale names """ locales = [] output = executeCommand(["locale"], ["-a"], returnOutput=True, ignoreStderr=True)[1] for line in output: locales.append(line.rstrip()) # pylint: disable=E1101 return locales ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/tools/__init__.py0000644000000000000000000000342514567004737017447 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Tools # Purpose : Provides package initialization # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Official Cedar Backup Tools This package provides official Cedar Backup tools. Tools are things that feel a little like extensions, but don't fit the normal mold of extensions. For instance, they might not be intended to run from cron, or might need to interact dynamically with the user (i.e. accept user input). Tools are usually scripts that are run directly from the command line, just like the main ``cback3`` script. Like the ``cback3`` script, the majority of a tool is implemented in a .py module, and then the script just invokes the module's ``cli()`` function. The actual scripts for tools are distributed in the util/ directory. :author: Kenneth J. Pronovici """ ######################################################################## # Package initialization ######################################################################## # Using 'from CedarBackup3.tools import *' will just import the modules listed # in the __all__ variable. import CedarBackup3.tools.amazons3 import CedarBackup3.tools.span __all__ = ["span", "amazons3"] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/tools/amazons3.py0000644000000000000000000013610214567004737017442 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2014-2016,2020 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Cedar Backup tool to synchronize an Amazon S3 bucket. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Notes ######################################################################## """ Synchonizes a local directory with an Amazon S3 bucket. No configuration is required; all necessary information is taken from the command-line. The only thing configuration would help with is the path resolver interface, and it doesn't seem worth it to require configuration just to get that. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules and constants ######################################################################## import getopt import json import logging import os import sys import warnings from functools import total_ordering from pathlib import Path import chardet from CedarBackup3.cli import DEFAULT_LOGFILE, DEFAULT_MODE, DEFAULT_OWNERSHIP, setupLogging from CedarBackup3.filesystem import FilesystemList from CedarBackup3.release import AUTHOR, EMAIL, VERSION from CedarBackup3.util import Diagnostics, encodePath, executeCommand, splitCommandLine ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.tools.amazons3") AWS_COMMAND = ["aws"] SHORT_SWITCHES = "hVbql:o:m:OdsDvuw" LONG_SWITCHES = [ "help", "version", "verbose", "quiet", "logfile=", "owner=", "mode=", "output", "debug", "stack", "diagnostics", "verifyOnly", "uploadOnly", "ignoreWarnings", ] ####################################################################### # Options class ####################################################################### @total_ordering class Options(object): ###################### # Class documentation ###################### """ Class representing command-line options for the cback3-amazons3-sync script. The ``Options`` class is a Python object representation of the command-line options of the cback3-amazons3-sync script. The object representation is two-way: a command line string or a list of command line arguments can be used to create an ``Options`` object, and then changes to the object can be propogated back to a list of command-line arguments or to a command-line string. An ``Options`` object can even be created from scratch programmatically (if you have a need for that). There are two main levels of validation in the ``Options`` class. The first is field-level validation. Field-level validation comes into play when a given field in an object is assigned to or updated. We use Python's ``property`` functionality to enforce specific validations on field values, and in some places we even use customized list classes to enforce validations on list members. You should expect to catch a ``ValueError`` exception when making assignments to fields if you are programmatically filling an object. The second level of validation is post-completion validation. Certain validations don't make sense until an object representation of options is fully "complete". We don't want these validations to apply all of the time, because it would make building up a valid object from scratch a real pain. For instance, we might have to do things in the right order to keep from throwing exceptions, etc. All of these post-completion validations are encapsulated in the :any:`Options.validate` method. This method can be called at any time by a client, and will always be called immediately after creating a ``Options`` object from a command line and before exporting a ``Options`` object back to a command line. This way, we get acceptable ease-of-use but we also don't accept or emit invalid command lines. *Note:* Lists within this class are "unordered" for equality comparisons. """ ############## # Constructor ############## def __init__(self, argumentList=None, argumentString=None, validate=True): """ Initializes an options object. If you initialize the object without passing either ``argumentList`` or ``argumentString``, the object will be empty and will be invalid until it is filled in properly. No reference to the original arguments is saved off by this class. Once the data has been parsed (successfully or not) this original information is discarded. The argument list is assumed to be a list of arguments, not including the name of the command, something like ``sys.argv[1:]``. If you pass ``sys.argv`` instead, things are not going to work. The argument string will be parsed into an argument list by the :any:`util.splitCommandLine` function (see the documentation for that function for some important notes about its limitations). There is an assumption that the resulting list will be equivalent to ``sys.argv[1:]``, just like ``argumentList``. Unless the ``validate`` argument is ``False``, the :any:`Options.validate` method will be called (with its default arguments) after successfully parsing any passed-in command line. This validation ensures that appropriate actions, etc. have been specified. Keep in mind that even if ``validate`` is ``False``, it might not be possible to parse the passed-in command line, so an exception might still be raised. *Note:* The command line format is specified by the ``_usage`` function. Call ``_usage`` to see a usage statement for the cback3-amazons3-sync script. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to read in invalid command line arguments. Args: argumentList (List of arguments, i.e. ``sys.argv``): Command line for a program argumentString (String, i.e. "cback3-amazons3-sync --verbose stage store"): Command line for a program validate (Boolean true/false): Validate the command line after parsing it Raises: getopt.GetoptError: If the command-line arguments could not be parsed ValueError: If the command-line arguments are invalid """ self._help = False self._version = False self._verbose = False self._quiet = False self._logfile = None self._owner = None self._mode = None self._output = False self._debug = False self._stacktrace = False self._diagnostics = False self._verifyOnly = False self._uploadOnly = False self._ignoreWarnings = False self._sourceDir = None self._s3BucketUrl = None if argumentList is not None and argumentString is not None: raise ValueError("Use either argumentList or argumentString, but not both.") if argumentString is not None: argumentList = splitCommandLine(argumentString) if argumentList is not None: self._parseArgumentList(argumentList) if validate: self.validate() ######################### # String representations ######################### def __repr__(self): """ Official string representation for class instance. """ return self.buildArgumentString(validate=False) def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() ############################# # Standard comparison method ############################# def __eq__(self, other): """Equals operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, iplemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Lists within this class are "unordered" for equality comparisons. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ if other is None: return 1 if self.help != other.help: if self.help < other.help: return -1 else: return 1 if self.version != other.version: if self.version < other.version: return -1 else: return 1 if self.verbose != other.verbose: if self.verbose < other.verbose: return -1 else: return 1 if self.quiet != other.quiet: if self.quiet < other.quiet: return -1 else: return 1 if self.logfile != other.logfile: if str(self.logfile or "") < str(other.logfile or ""): return -1 else: return 1 if self.owner != other.owner: if str(self.owner or "") < str(other.owner or ""): return -1 else: return 1 if self.mode != other.mode: if int(self.mode or 0) < int(other.mode or 0): return -1 else: return 1 if self.output != other.output: if self.output < other.output: return -1 else: return 1 if self.debug != other.debug: if self.debug < other.debug: return -1 else: return 1 if self.stacktrace != other.stacktrace: if self.stacktrace < other.stacktrace: return -1 else: return 1 if self.diagnostics != other.diagnostics: if self.diagnostics < other.diagnostics: return -1 else: return 1 if self.verifyOnly != other.verifyOnly: if self.verifyOnly < other.verifyOnly: return -1 else: return 1 if self.uploadOnly != other.uploadOnly: if self.uploadOnly < other.uploadOnly: return -1 else: return 1 if self.ignoreWarnings != other.ignoreWarnings: if self.ignoreWarnings < other.ignoreWarnings: return -1 else: return 1 if self.sourceDir != other.sourceDir: if str(self.sourceDir or "") < str(other.sourceDir or ""): return -1 else: return 1 if self.s3BucketUrl != other.s3BucketUrl: if str(self.s3BucketUrl or "") < str(other.s3BucketUrl or ""): return -1 else: return 1 return 0 ############# # Properties ############# def _setHelp(self, value): """ Property target used to set the help flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._help = True else: self._help = False def _getHelp(self): """ Property target used to get the help flag. """ return self._help def _setVersion(self, value): """ Property target used to set the version flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._version = True else: self._version = False def _getVersion(self): """ Property target used to get the version flag. """ return self._version def _setVerbose(self, value): """ Property target used to set the verbose flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._verbose = True else: self._verbose = False def _getVerbose(self): """ Property target used to get the verbose flag. """ return self._verbose def _setQuiet(self, value): """ Property target used to set the quiet flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._quiet = True else: self._quiet = False def _getQuiet(self): """ Property target used to get the quiet flag. """ return self._quiet def _setLogfile(self, value): """ Property target used to set the logfile parameter. Raises: ValueError: If the value cannot be encoded properly """ if value is not None: if len(value) < 1: raise ValueError("The logfile parameter must be a non-empty string.") self._logfile = encodePath(value) def _getLogfile(self): """ Property target used to get the logfile parameter. """ return self._logfile def _setOwner(self, value): """ Property target used to set the owner parameter. If not ``None``, the owner must be a ``(user,group)`` tuple or list. Strings (and inherited children of strings) are explicitly disallowed. The value will be normalized to a tuple. Raises: ValueError: If the value is not valid """ if value is None: self._owner = None else: if isinstance(value, str): raise ValueError("Must specify user and group tuple for owner parameter.") if len(value) != 2: raise ValueError("Must specify user and group tuple for owner parameter.") if len(value[0]) < 1 or len(value[1]) < 1: raise ValueError("User and group tuple values must be non-empty strings.") self._owner = (value[0], value[1]) def _getOwner(self): """ Property target used to get the owner parameter. The parameter is a tuple of ``(user, group)``. """ return self._owner def _setMode(self, value): """ Property target used to set the mode parameter. """ if value is None: self._mode = None else: try: if isinstance(value, str): value = int(value, 8) else: value = int(value) except TypeError: raise ValueError("Mode must be an octal integer >= 0, i.e. 644.") if value < 0: raise ValueError("Mode must be an octal integer >= 0. i.e. 644.") self._mode = value def _getMode(self): """ Property target used to get the mode parameter. """ return self._mode def _setOutput(self, value): """ Property target used to set the output flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._output = True else: self._output = False def _getOutput(self): """ Property target used to get the output flag. """ return self._output def _setDebug(self, value): """ Property target used to set the debug flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._debug = True else: self._debug = False def _getDebug(self): """ Property target used to get the debug flag. """ return self._debug def _setStacktrace(self, value): """ Property target used to set the stacktrace flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._stacktrace = True else: self._stacktrace = False def _getStacktrace(self): """ Property target used to get the stacktrace flag. """ return self._stacktrace def _setDiagnostics(self, value): """ Property target used to set the diagnostics flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._diagnostics = True else: self._diagnostics = False def _getDiagnostics(self): """ Property target used to get the diagnostics flag. """ return self._diagnostics def _setVerifyOnly(self, value): """ Property target used to set the verifyOnly flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._verifyOnly = True else: self._verifyOnly = False def _getVerifyOnly(self): """ Property target used to get the verifyOnly flag. """ return self._verifyOnly def _setUploadOnly(self, value): """ Property target used to set the uploadOnly flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._uploadOnly = True else: self._uploadOnly = False def _getUploadOnly(self): """ Property target used to get the uploadOnly flag. """ return self._uploadOnly def _setIgnoreWarnings(self, value): """ Property target used to set the ignoreWarnings flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._ignoreWarnings = True else: self._ignoreWarnings = False def _getIgnoreWarnings(self): """ Property target used to get the ignoreWarnings flag. """ return self._ignoreWarnings def _setSourceDir(self, value): """ Property target used to set the sourceDir parameter. """ if value is not None: if len(value) < 1: raise ValueError("The sourceDir parameter must be a non-empty string.") self._sourceDir = value def _getSourceDir(self): """ Property target used to get the sourceDir parameter. """ return self._sourceDir def _setS3BucketUrl(self, value): """ Property target used to set the s3BucketUrl parameter. """ if value is not None: if len(value) < 1: raise ValueError("The s3BucketUrl parameter must be a non-empty string.") self._s3BucketUrl = value def _getS3BucketUrl(self): """ Property target used to get the s3BucketUrl parameter. """ return self._s3BucketUrl help = property(_getHelp, _setHelp, None, "Command-line help (``-h,--help``) flag.") version = property(_getVersion, _setVersion, None, "Command-line version (``-V,--version``) flag.") verbose = property(_getVerbose, _setVerbose, None, "Command-line verbose (``-b,--verbose``) flag.") quiet = property(_getQuiet, _setQuiet, None, "Command-line quiet (``-q,--quiet``) flag.") logfile = property(_getLogfile, _setLogfile, None, "Command-line logfile (``-l,--logfile``) parameter.") owner = property(_getOwner, _setOwner, None, "Command-line owner (``-o,--owner``) parameter, as tuple ``(user,group)``.") mode = property(_getMode, _setMode, None, "Command-line mode (``-m,--mode``) parameter.") output = property(_getOutput, _setOutput, None, "Command-line output (``-O,--output``) flag.") debug = property(_getDebug, _setDebug, None, "Command-line debug (``-d,--debug``) flag.") stacktrace = property(_getStacktrace, _setStacktrace, None, "Command-line stacktrace (``-s,--stack``) flag.") diagnostics = property(_getDiagnostics, _setDiagnostics, None, "Command-line diagnostics (``-D,--diagnostics``) flag.") verifyOnly = property(_getVerifyOnly, _setVerifyOnly, None, "Command-line verifyOnly (``-v,--verifyOnly``) flag.") uploadOnly = property(_getUploadOnly, _setUploadOnly, None, "Command-line uploadOnly (``-u,--uploadOnly``) flag.") ignoreWarnings = property( _getIgnoreWarnings, _setIgnoreWarnings, None, "Command-line ignoreWarnings (``-w,--ignoreWarnings``) flag" ) sourceDir = property(_getSourceDir, _setSourceDir, None, "Command-line sourceDir, source of sync.") s3BucketUrl = property(_getS3BucketUrl, _setS3BucketUrl, None, "Command-line s3BucketUrl, target of sync.") ################## # Utility methods ################## def validate(self): """ Validates command-line options represented by the object. Unless ``--help`` or ``--version`` are supplied, at least one action must be specified. Other validations (as for allowed values for particular options) will be taken care of at assignment time by the properties functionality. *Note:* The command line format is specified by the ``_usage`` function. Call ``_usage`` to see a usage statement for the cback3-amazons3-sync script. Raises: ValueError: If one of the validations fails """ if not self.help and not self.version and not self.diagnostics: if self.sourceDir is None or self.s3BucketUrl is None: raise ValueError("Source directory and S3 bucket URL are both required.") def buildArgumentList(self, validate=True): """ Extracts options into a list of command line arguments. The original order of the various arguments (if, indeed, the object was initialized with a command-line) is not preserved in this generated argument list. Besides that, the argument list is normalized to use the long option names (i.e. --version rather than -V). The resulting list will be suitable for passing back to the constructor in the ``argumentList`` parameter. Unlike :any:`buildArgumentString`, string arguments are not quoted here, because there is no need for it. Unless the ``validate`` parameter is ``False``, the :any:`Options.validate` method will be called (with its default arguments) against the options before extracting the command line. If the options are not valid, then an argument list will not be extracted. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to extract an invalid command line. Args: validate (Boolean true/false): Validate the options before extracting the command line Returns: List representation of command-line arguments Raises: ValueError: If options within the object are invalid """ if validate: self.validate() argumentList = [] if self._help: argumentList.append("--help") if self.version: argumentList.append("--version") if self.verbose: argumentList.append("--verbose") if self.quiet: argumentList.append("--quiet") if self.logfile is not None: argumentList.append("--logfile") argumentList.append(self.logfile) if self.owner is not None: argumentList.append("--owner") argumentList.append("%s:%s" % (self.owner[0], self.owner[1])) if self.mode is not None: argumentList.append("--mode") argumentList.append("%o" % self.mode) if self.output: argumentList.append("--output") if self.debug: argumentList.append("--debug") if self.stacktrace: argumentList.append("--stack") if self.diagnostics: argumentList.append("--diagnostics") if self.verifyOnly: argumentList.append("--verifyOnly") if self.uploadOnly: argumentList.append("--uploadOnly") if self.ignoreWarnings: argumentList.append("--ignoreWarnings") if self.sourceDir is not None: argumentList.append(self.sourceDir) if self.s3BucketUrl is not None: argumentList.append(self.s3BucketUrl) return argumentList def buildArgumentString(self, validate=True): """ Extracts options into a string of command-line arguments. The original order of the various arguments (if, indeed, the object was initialized with a command-line) is not preserved in this generated argument string. Besides that, the argument string is normalized to use the long option names (i.e. --version rather than -V) and to quote all string arguments with double quotes (``"``). The resulting string will be suitable for passing back to the constructor in the ``argumentString`` parameter. Unless the ``validate`` parameter is ``False``, the :any:`Options.validate` method will be called (with its default arguments) against the options before extracting the command line. If the options are not valid, then an argument string will not be extracted. *Note:* It is strongly suggested that the ``validate`` option always be set to ``True`` (the default) unless there is a specific need to extract an invalid command line. Args: validate (Boolean true/false): Validate the options before extracting the command line Returns: String representation of command-line arguments Raises: ValueError: If options within the object are invalid """ if validate: self.validate() argumentString = "" if self._help: argumentString += "--help " if self.version: argumentString += "--version " if self.verbose: argumentString += "--verbose " if self.quiet: argumentString += "--quiet " if self.logfile is not None: argumentString += '--logfile "%s" ' % self.logfile if self.owner is not None: argumentString += '--owner "%s:%s" ' % (self.owner[0], self.owner[1]) if self.mode is not None: argumentString += "--mode %o " % self.mode if self.output: argumentString += "--output " if self.debug: argumentString += "--debug " if self.stacktrace: argumentString += "--stack " if self.diagnostics: argumentString += "--diagnostics " if self.verifyOnly: argumentString += "--verifyOnly " if self.uploadOnly: argumentString += "--uploadOnly " if self.ignoreWarnings: argumentString += "--ignoreWarnings " if self.sourceDir is not None: argumentString += '"%s" ' % self.sourceDir if self.s3BucketUrl is not None: argumentString += '"%s" ' % self.s3BucketUrl return argumentString def _parseArgumentList(self, argumentList): """ Internal method to parse a list of command-line arguments. Most of the validation we do here has to do with whether the arguments can be parsed and whether any values which exist are valid. We don't do any validation as to whether required elements exist or whether elements exist in the proper combination (instead, that's the job of the :any:`validate` method). For any of the options which supply parameters, if the option is duplicated with long and short switches (i.e. ``-l`` and a ``--logfile``) then the long switch is used. If the same option is duplicated with the same switch (long or short), then the last entry on the command line is used. Args: argumentList (List of arguments to a command, i.e. ``sys.argv[1:]``): List of arguments to a command Raises: ValueError: If the argument list cannot be successfully parsed """ switches = {} opts, remaining = getopt.getopt(argumentList, SHORT_SWITCHES, LONG_SWITCHES) for o, a in opts: # push the switches into a hash switches[o] = a if "-h" in switches or "--help" in switches: self.help = True if "-V" in switches or "--version" in switches: self.version = True if "-b" in switches or "--verbose" in switches: self.verbose = True if "-q" in switches or "--quiet" in switches: self.quiet = True if "-l" in switches: self.logfile = switches["-l"] if "--logfile" in switches: self.logfile = switches["--logfile"] if "-o" in switches: self.owner = switches["-o"].split(":", 1) if "--owner" in switches: self.owner = switches["--owner"].split(":", 1) if "-m" in switches: self.mode = switches["-m"] if "--mode" in switches: self.mode = switches["--mode"] if "-O" in switches or "--output" in switches: self.output = True if "-d" in switches or "--debug" in switches: self.debug = True if "-s" in switches or "--stack" in switches: self.stacktrace = True if "-D" in switches or "--diagnostics" in switches: self.diagnostics = True if "-v" in switches or "--verifyOnly" in switches: self.verifyOnly = True if "-u" in switches or "--uploadOnly" in switches: self.uploadOnly = True if "-w" in switches or "--ignoreWarnings" in switches: self.ignoreWarnings = True try: (self.sourceDir, self.s3BucketUrl) = remaining except ValueError: pass ####################################################################### # Public functions ####################################################################### ################# # cli() function ################# def cli(): """ Implements the command-line interface for the ``cback3-amazons3-sync`` script. Essentially, this is the "main routine" for the cback3-amazons3-sync script. It does all of the argument processing for the script, and then also implements the tool functionality. This function looks pretty similiar to ``CedarBackup3.cli.cli()``. It's not easy to refactor this code to make it reusable and also readable, so I've decided to just live with the duplication. A different error code is returned for each type of failure: - ``1``: The Python interpreter version is not supported - ``2``: Error processing command-line arguments - ``3``: Error configuring logging - ``5``: Backup was interrupted with a CTRL-C or similar - ``6``: Error executing other parts of the script *Note:* This script uses print rather than logging to the INFO level, because it is interactive. Underlying Cedar Backup functionality uses the logging mechanism exclusively. Returns: Error code as described above """ try: if list(map(int, [sys.version_info[0], sys.version_info[1]])) < [3, 8]: sys.stderr.write("Python 3 version 3.8 or greater required.\n") return 1 except: # sys.version_info isn't available before 2.0 sys.stderr.write("Python 3 version 3.8 or greater required.\n") return 1 try: options = Options(argumentList=sys.argv[1:]) except Exception as e: _usage() sys.stderr.write(" *** Error: %s\n" % e) return 2 if options.help: _usage() return 0 if options.version: _version() return 0 if options.diagnostics: _diagnostics() return 0 if options.stacktrace: logfile = setupLogging(options) else: try: logfile = setupLogging(options) except Exception as e: sys.stderr.write("Error setting up logging: %s\n" % e) return 3 logger.info("Cedar Backup Amazon S3 sync run started.") logger.info("Options were [%s]", options) logger.info("Logfile is [%s]", logfile) Diagnostics().logDiagnostics(method=logger.info) if options.stacktrace: _executeAction(options) else: try: _executeAction(options) except KeyboardInterrupt: logger.error("Backup interrupted.") logger.info("Cedar Backup Amazon S3 sync run completed with status 5.") return 5 except Exception as e: logger.error("Error executing backup: %s", e) logger.info("Cedar Backup Amazon S3 sync run completed with status 6.") return 6 logger.info("Cedar Backup Amazon S3 sync run completed with status 0.") return 0 ####################################################################### # Utility functions ####################################################################### #################### # _usage() function #################### def _usage(fd=sys.stderr): """ Prints usage information for the cback3-amazons3-sync script. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write(" Usage: cback3-amazons3-sync [switches] sourceDir s3bucketUrl\n") fd.write("\n") fd.write(" Cedar Backup Amazon S3 sync tool.\n") fd.write("\n") fd.write(" This Cedar Backup utility synchronizes a local directory to an Amazon S3\n") fd.write(" bucket. After the sync is complete, a validation step is taken. An\n") fd.write(" error is reported if the contents of the bucket do not match the\n") fd.write(" source directory, or if the indicated size for any file differs.\n") fd.write(" This tool is a wrapper over the AWS CLI command-line tool.\n") fd.write("\n") fd.write(" The following arguments are required:\n") fd.write("\n") fd.write(" sourceDir The local source directory on disk (must exist)\n") fd.write(" s3BucketUrl The URL to the target Amazon S3 bucket\n") fd.write("\n") fd.write(" The following switches are accepted:\n") fd.write("\n") fd.write(" -h, --help Display this usage/help listing\n") fd.write(" -V, --version Display version information\n") fd.write(" -b, --verbose Print verbose output as well as logging to disk\n") fd.write(" -q, --quiet Run quietly (display no output to the screen)\n") fd.write(" -l, --logfile Path to logfile (default: %s)\n" % DEFAULT_LOGFILE) fd.write( " -o, --owner Logfile ownership, user:group (default: %s:%s)\n" % (DEFAULT_OWNERSHIP[0], DEFAULT_OWNERSHIP[1]) ) fd.write(" -m, --mode Octal logfile permissions mode (default: %o)\n" % DEFAULT_MODE) fd.write(" -O, --output Record some sub-command (i.e. aws) output to the log\n") fd.write(" -d, --debug Write debugging information to the log (implies --output)\n") fd.write( " -s, --stack Dump Python stack trace instead of swallowing exceptions\n" ) # exactly 80 characters in width! fd.write(" -D, --diagnostics Print runtime diagnostics to the screen and exit\n") fd.write(" -v, --verifyOnly Only verify the S3 bucket contents, do not make changes\n") fd.write(" -u, --uploadOnly Only upload new data, do not remove files in the S3 bucket\n") fd.write(" -w, --ignoreWarnings Ignore warnings about problematic filename encodings\n") fd.write("\n") fd.write(" Typical usage would be something like:\n") fd.write("\n") fd.write(" cback3-amazons3-sync /home/myuser s3://example.com-backup/myuser\n") fd.write("\n") fd.write(" This will sync the contents of /home/myuser into the indicated bucket.\n") fd.write("\n") ###################### # _version() function ###################### def _version(fd=sys.stdout): """ Prints version information for the cback3-amazons3-sync script. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write(" Cedar Backup Amazon S3 sync tool.\n") fd.write(" Included with Cedar Backup version %s.\n" % VERSION) fd.write("\n") fd.write(" Copyright (c) %s <%s>.\n" % (AUTHOR, EMAIL)) fd.write(" See NOTICE for a list of included code and other contributors.\n") fd.write(" This is free software; there is NO warranty. See the\n") fd.write(" GNU General Public License version 2 for copying conditions.\n") fd.write("\n") fd.write(" Use the --help option for usage information.\n") fd.write("\n") ########################## # _diagnostics() function ########################## def _diagnostics(fd=sys.stdout): """ Prints runtime diagnostics information. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write("Diagnostics:\n") fd.write("\n") Diagnostics().printDiagnostics(fd=fd, prefix=" ") fd.write("\n") ############################ # _executeAction() function ############################ def _executeAction(options): """ Implements the guts of the cback3-amazons3-sync tool. Args: options (Options object): Program command-line options Raises: Exception: Under many generic error conditions """ sourceFiles = _buildSourceFiles(options.sourceDir) if not options.ignoreWarnings: _checkSourceFiles(options.sourceDir, sourceFiles) if not options.verifyOnly: _synchronizeBucket(options.sourceDir, options.s3BucketUrl, options.uploadOnly) _verifyBucketContents(options.sourceDir, sourceFiles, options.s3BucketUrl) ################################ # _buildSourceFiles() function ################################ def _buildSourceFiles(sourceDir): """ Build a list of files in a source directory Args: sourceDir: Local source directory Returns: FilesystemList with contents of source directory """ if not os.path.isdir(sourceDir): raise ValueError("Source directory does not exist on disk.") sourceFiles = FilesystemList() sourceFiles.addDirContents(sourceDir) return sourceFiles ############################### # _checkSourceFiles() function ############################### # pylint: disable=W0613 def _checkSourceFiles(sourceDir, sourceFiles): """ Check source files, trying to guess which ones will have encoding problems. Args: sourceDir: Local source directory sourceDir: Local source directory @raises ValueError: If a problem file is found @see U{http://opensourcehacker.com/2011/09/16/fix-linux-filename-encodings-with-python/} @see U{http://serverfault.com/questions/82821/how-to-tell-the-language-encoding-of-a-filename-on-linux} @see U{http://randysofia.com/2014/06/06/aws-cli-and-your-locale/} """ with warnings.catch_warnings(): encoding = Diagnostics().encoding # Note: this was difficult to fully test. As of the original Python 2 # implementation, I had a bunch of files on disk that had inconsistent # encodings, so I was able to prove that the check warned about these # files initially, and then didn't warn after I fixed them. I didn't # save off those files for a unit test (ugh) so by the time of the Python # 3 conversion -- which is subtly different because of the different way # Python 3 handles unicode strings -- I had to contrive some tests. I # think the tests I wrote are consistent with the earlier problems, and I # do get the same result for those tests in both CedarBackup 2 and Cedar # Backup 3. However, I can't be certain the implementation is # equivalent. If someone runs into a situation that this code doesn't # handle, you may need to revisit the implementation. failed = False for entry in sourceFiles: path = bytes(Path(entry)) result = chardet.detect(path) source = path.decode(result["encoding"]) try: target = path.decode(encoding) if source != target: logger.error("Inconsistent encoding for [%s]: got %s, but need %s", path, result["encoding"], encoding) failed = True except Exception: logger.error("Inconsistent encoding for [%s]: got %s, but need %s", path, result["encoding"], encoding) failed = True if not failed: logger.info("Completed checking source filename encoding (no problems found).") else: logger.error("Some filenames have inconsistent encodings and will likely cause sync problems.") logger.error("You may be able to fix this by setting a more sensible locale in your environment.") logger.error("Aternately, you can rename the problem files to be valid in the indicated locale.") logger.error("To ignore this warning and proceed anyway, use --ignoreWarnings") raise ValueError("Some filenames have inconsistent encodings and will likely cause sync problems.") ################################ # _synchronizeBucket() function ################################ def _synchronizeBucket(sourceDir, s3BucketUrl, uploadOnly): """ Synchronize a local directory to an Amazon S3 bucket. Args: sourceDir: Local source directory s3BucketUrl: Target S3 bucket URL """ # Since at least early 2015, 'aws s3 sync' is always recursive and the # --recursive option is useless. They eventually removed it and now using # it causes an error. See: https://github.com/aws/aws-cli/issues/1170 logger.info("Synchronizing local source directory up to Amazon S3.") args = ["s3", "sync", sourceDir, s3BucketUrl] if uploadOnly: logger.info("Sync process will only upload new data, never removing files in S3") else: logger.info("This will be a full sync process, removing any S3 files that do not exist in the source") args += ["--delete"] result = executeCommand(AWS_COMMAND, args, returnOutput=False)[0] if result != 0: raise IOError("Error [%d] calling AWS CLI synchronize bucket." % result) ################################### # _verifyBucketContents() function ################################### def _verifyBucketContents(sourceDir, sourceFiles, s3BucketUrl): """ Verify that a source directory is equivalent to an Amazon S3 bucket. Args: sourceDir: Local source directory sourceFiles: Filesystem list containing contents of source directory s3BucketUrl: Target S3 bucket URL """ # As of this writing, the documentation for the S3 API that we're using # below says that up to 1000 elements at a time are returned, and that we # have to manually handle pagination by looking for the IsTruncated element. # However, in practice, this is not true. I have been testing with # "aws-cli/1.4.4 Python/2.7.3 Linux/3.2.0-4-686-pae", installed through PIP. # No matter how many items exist in my bucket and prefix, I get back a # single JSON result. I've tested with buckets containing nearly 6000 # elements. # # If I turn on debugging, it's clear that underneath, something in the API # is executing multiple list-object requests against AWS, and stiching # results together to give me back the final JSON result. The debug output # clearly incldues multiple requests, and each XML response (except for the # final one) contains true. # # This feature is not mentioned in the offical changelog for any of the # releases going back to 1.0.0. It appears to happen in the botocore # library, but I'll admit I can't actually find the code that implements it. # For now, all I can do is rely on this behavior and hope that the # documentation is out-of-date. I'm not going to write code that tries to # parse out IsTruncated if I can't actually test that code. (bucket, prefix) = s3BucketUrl.replace("s3://", "").split("/", 1) query = "Contents[].{Key: Key, Size: Size}" args = ["s3api", "list-objects", "--bucket", bucket, "--prefix", prefix, "--query", query] (result, data) = executeCommand(AWS_COMMAND, args, returnOutput=True) if result != 0: raise IOError("Error [%d] calling AWS CLI verify bucket contents." % result) contents = {} for entry in json.loads("".join(data)): key = entry["Key"].replace(prefix, "") size = int(entry["Size"]) contents[key] = size failed = False for entry in sourceFiles: if os.path.isfile(entry): key = entry.replace(sourceDir, "") size = int(os.stat(entry).st_size) if key not in contents: logger.error("File was apparently not uploaded: [%s]", entry) failed = True else: if size != contents[key]: logger.error("File size differs [%s]: expected %s bytes but got %s bytes", entry, size, contents[key]) failed = True if not failed: logger.info("Completed verifying Amazon S3 bucket contents (no problems found).") else: logger.error("There were differences between source directory and target S3 bucket.") raise ValueError("There were differences between source directory and target S3 bucket.") ######################################################################### # Main routine ######################################################################## if __name__ == "__main__": sys.exit(cli()) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/tools/span.py0000644000000000000000000006277614567004737016667 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Spans staged data among multiple discs # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Notes ######################################################################## """ Spans staged data among multiple discs This is the Cedar Backup span tool. It is intended for use by people who stage more data than can fit on a single disc. It allows a user to split staged data among more than one disc. It can't be an extension because it requires user input when switching media. Most configuration is taken from the Cedar Backup configuration file, specifically the store section. A few pieces of configuration are taken directly from the user. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules and constants ######################################################################## import logging import os import sys import tempfile from CedarBackup3.actions.constants import STORE_INDICATOR from CedarBackup3.actions.store import writeIndicatorFile from CedarBackup3.actions.util import createWriter, findDailyDirs from CedarBackup3.cli import ( DEFAULT_CONFIG, DEFAULT_LOGFILE, DEFAULT_MODE, DEFAULT_OWNERSHIP, Options, setupLogging, setupPathResolver, ) from CedarBackup3.config import Config from CedarBackup3.filesystem import BackupFileList, compareDigestMaps, normalizeDir from CedarBackup3.release import AUTHOR, EMAIL, VERSION from CedarBackup3.util import UNIT_BYTES, UNIT_SECTORS, Diagnostics, convertSize, displayBytes, mount, unmount ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.tools.span") ####################################################################### # SpanOptions class ####################################################################### class SpanOptions(Options): """ Tool-specific command-line options. Most of the cback3 command-line options are exactly what we need here -- logfile path, permissions, verbosity, etc. However, we need to make a few tweaks since we don't accept any actions. Also, a few extra command line options that we accept are really ignored underneath. I just don't care about that for a tool like this. """ def validate(self): """ Validates command-line options represented by the object. There are no validations here, because we don't use any actions. Raises: ValueError: If one of the validations fails """ pass ####################################################################### # Public functions ####################################################################### ################# # cli() function ################# def cli(): """ Implements the command-line interface for the ``cback3-span`` script. Essentially, this is the "main routine" for the cback3-span script. It does all of the argument processing for the script, and then also implements the tool functionality. This function looks pretty similiar to ``CedarBackup3.cli.cli()``. It's not easy to refactor this code to make it reusable and also readable, so I've decided to just live with the duplication. A different error code is returned for each type of failure: - ``1``: The Python interpreter version is not supported - ``2``: Error processing command-line arguments - ``3``: Error configuring logging - ``4``: Error parsing indicated configuration file - ``5``: Backup was interrupted with a CTRL-C or similar - ``6``: Error executing other parts of the script *Note:* This script uses print rather than logging to the INFO level, because it is interactive. Underlying Cedar Backup functionality uses the logging mechanism exclusively. Returns: Error code as described above """ try: if list(map(int, [sys.version_info[0], sys.version_info[1]])) < [3, 8]: sys.stderr.write("Python 3 version 3.8 or greater required.\n") return 1 except: # sys.version_info isn't available before 2.0 sys.stderr.write("Python 3 version 3.8 or greater required.\n") return 1 try: options = SpanOptions(argumentList=sys.argv[1:]) except Exception as e: _usage() sys.stderr.write(" *** Error: %s\n" % e) return 2 if options.help: _usage() return 0 if options.version: _version() return 0 if options.diagnostics: _diagnostics() return 0 if options.stacktrace: logfile = setupLogging(options) else: try: logfile = setupLogging(options) except Exception as e: sys.stderr.write("Error setting up logging: %s\n" % e) return 3 logger.info("Cedar Backup 'span' utility run started.") logger.info("Options were [%s]", options) logger.info("Logfile is [%s]", logfile) if options.config is None: logger.debug("Using default configuration file.") configPath = DEFAULT_CONFIG else: logger.debug("Using user-supplied configuration file.") configPath = options.config try: logger.info("Configuration path is [%s]", configPath) config = Config(xmlPath=configPath) setupPathResolver(config) except Exception as e: logger.error("Error reading or handling configuration: %s", e) logger.info("Cedar Backup 'span' utility run completed with status 4.") return 4 if options.stacktrace: _executeAction(options, config) else: try: _executeAction(options, config) except KeyboardInterrupt: logger.error("Backup interrupted.") logger.info("Cedar Backup 'span' utility run completed with status 5.") return 5 except Exception as e: logger.error("Error executing backup: %s", e) logger.info("Cedar Backup 'span' utility run completed with status 6.") return 6 logger.info("Cedar Backup 'span' utility run completed with status 0.") return 0 ####################################################################### # Utility functions ####################################################################### #################### # _usage() function #################### def _usage(fd=sys.stderr): """ Prints usage information for the cback3-span script. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write(" Usage: cback3-span [switches]\n") fd.write("\n") fd.write(" Cedar Backup 'span' tool.\n") fd.write("\n") fd.write(" This Cedar Backup utility spans staged data between multiple discs.\n") fd.write(" It is a utility, not an extension, and requires user interaction.\n") fd.write("\n") fd.write(" The following switches are accepted, mostly to set up underlying\n") fd.write(" Cedar Backup functionality:\n") fd.write("\n") fd.write(" -h, --help Display this usage/help listing\n") fd.write(" -V, --version Display version information\n") fd.write(" -b, --verbose Print verbose output as well as logging to disk\n") fd.write(" -c, --config Path to config file (default: %s)\n" % DEFAULT_CONFIG) fd.write(" -l, --logfile Path to logfile (default: %s)\n" % DEFAULT_LOGFILE) fd.write(" -o, --owner Logfile ownership, user:group (default: %s:%s)\n" % (DEFAULT_OWNERSHIP[0], DEFAULT_OWNERSHIP[1])) fd.write(" -m, --mode Octal logfile permissions mode (default: %o)\n" % DEFAULT_MODE) fd.write(" -O, --output Record some sub-command (i.e. tar) output to the log\n") fd.write(" -d, --debug Write debugging information to the log (implies --output)\n") fd.write(" -s, --stack Dump a Python stack trace instead of swallowing exceptions\n") fd.write("\n") ###################### # _version() function ###################### def _version(fd=sys.stdout): """ Prints version information for the cback3-span script. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write(" Cedar Backup 'span' tool.\n") fd.write(" Included with Cedar Backup version %s.\n" % VERSION) fd.write("\n") fd.write(" Copyright (c) %s <%s>.\n" % (AUTHOR, EMAIL)) fd.write(" See NOTICE for a list of included code and other contributors.\n") fd.write(" This is free software; there is NO warranty. See the\n") fd.write(" GNU General Public License version 2 for copying conditions.\n") fd.write("\n") fd.write(" Use the --help option for usage information.\n") fd.write("\n") ########################## # _diagnostics() function ########################## def _diagnostics(fd=sys.stdout): """ Prints runtime diagnostics information. Args: fd: File descriptor used to print information *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ fd.write("\n") fd.write("Diagnostics:\n") fd.write("\n") Diagnostics().printDiagnostics(fd=fd, prefix=" ") fd.write("\n") ############################ # _executeAction() function ############################ # pylint: disable=W0613 def _executeAction(options, config): """ Implements the guts of the cback3-span tool. Args: options (SpanOptions object): Program command-line options config (Config object): Program configuration Raises: Exception: Under many generic error conditions """ print("") print("================================================") print(" Cedar Backup 'span' tool") print("================================================") print("") print("This the Cedar Backup span tool. It is used to split up staging") print("data when that staging data does not fit onto a single disc.") print("") print("This utility operates using Cedar Backup configuration. Configuration") print("specifies which staging directory to look at and which writer device") print("and media type to use.") print("") if not _getYesNoAnswer("Continue?", default="Y"): return print("===") print("") print("Cedar Backup store configuration looks like this:") print("") print(" Source Directory...: %s" % config.store.sourceDir) print(" Media Type.........: %s" % config.store.mediaType) print(" Device Type........: %s" % config.store.deviceType) print(" Device Path........: %s" % config.store.devicePath) print(" Device SCSI ID.....: %s" % config.store.deviceScsiId) print(" Drive Speed........: %s" % config.store.driveSpeed) print(" Check Data Flag....: %s" % config.store.checkData) print(" No Eject Flag......: %s" % config.store.noEject) print("") if not _getYesNoAnswer("Is this OK?", default="Y"): return print("===") (writer, mediaCapacity) = _getWriter(config) print("") print("Please wait, indexing the source directory (this may take a while)...") (dailyDirs, fileList) = _findDailyDirs(config.store.sourceDir) print("===") print("") print("The following daily staging directories have not yet been written to disc:") print("") for dailyDir in dailyDirs: print(" %s" % dailyDir) totalSize = fileList.totalSize() print("") print("The total size of the data in these directories is %s." % displayBytes(totalSize)) print("") if not _getYesNoAnswer("Continue?", default="Y"): return print("===") print("") print("Based on configuration, the capacity of your media is %s." % displayBytes(mediaCapacity)) print("") print("Since estimates are not perfect and there is some uncertainly in") print('media capacity calculations, it is good to have a "cushion",') print("a percentage of capacity to set aside. The cushion reduces the") print("capacity of your media, so a 1.5% cushion leaves 98.5% remaining.") print("") cushion = _getFloat("What cushion percentage?", default=4.5) print("===") realCapacity = ((100.0 - cushion) / 100.0) * mediaCapacity minimumDiscs = (totalSize / realCapacity) + 1 print("") print("The real capacity, taking into account the %.2f%% cushion, is %s." % (cushion, displayBytes(realCapacity))) print("It will take at least %d disc(s) to store your %s of data." % (minimumDiscs, displayBytes(totalSize))) print("") if not _getYesNoAnswer("Continue?", default="Y"): return print("===") happy = False while not happy: print("") print("Which algorithm do you want to use to span your data across") print("multiple discs?") print("") print("The following algorithms are available:") print("") print(' first....: The "first-fit" algorithm') print(' best.....: The "best-fit" algorithm') print(' worst....: The "worst-fit" algorithm') print(' alternate: The "alternate-fit" algorithm') print("") print("If you don't like the results you will have a chance to try a") print("different one later.") print("") algorithm = _getChoiceAnswer("Which algorithm?", "worst", ["first", "best", "worst", "alternate"]) print("===") print("") print("Please wait, generating file lists (this may take a while)...") spanSet = fileList.generateSpan(capacity=realCapacity, algorithm="%s_fit" % algorithm) print("===") print("") print('Using the "%s-fit" algorithm, Cedar Backup can split your data' % algorithm) print("into %d discs." % len(spanSet)) print("") counter = 0 for item in spanSet: counter += 1 print( "Disc %d: %d files, %s, %.2f%% utilization" % (counter, len(item.fileList), displayBytes(item.size), item.utilization) ) print("") if _getYesNoAnswer("Accept this solution?", default="Y"): happy = True print("===") counter = 0 for spanItem in spanSet: counter += 1 if counter == 1: print("") _getReturn("Please place the first disc in your backup device.\nPress return when ready.") print("===") else: print("") _getReturn("Please replace the disc in your backup device.\nPress return when ready.") print("===") _writeDisc(config, writer, spanItem) _writeStoreIndicator(config, dailyDirs) print("") print("Completed writing all discs.") ############################ # _findDailyDirs() function ############################ def _findDailyDirs(stagingDir): """ Returns a list of all daily staging directories that have not yet been stored. The store indicator file ``cback.store`` will be written to a daily staging directory once that directory is written to disc. So, this function looks at each daily staging directory within the configured staging directory, and returns a list of those which do not contain the indicator file. Returned is a tuple containing two items: a list of daily staging directories, and a BackupFileList containing all files among those staging directories. Args: stagingDir: Configured staging directory Returns: Tuple (staging dirs, backup file list) """ results = findDailyDirs(stagingDir, STORE_INDICATOR) fileList = BackupFileList() for item in results: fileList.addDirContents(item) return (results, fileList) ################################## # _writeStoreIndicator() function ################################## def _writeStoreIndicator(config, dailyDirs): """ Writes a store indicator file into daily directories. Args: config: Config object dailyDirs: List of daily directories """ for dailyDir in dailyDirs: writeIndicatorFile(dailyDir, STORE_INDICATOR, config.options.backupUser, config.options.backupGroup) ######################## # _getWriter() function ######################## def _getWriter(config): """ Gets a writer and media capacity from store configuration. Returned is a writer and a media capacity in bytes. Args: config: Cedar Backup configuration Returns: Tuple of (writer, mediaCapacity) """ writer = createWriter(config) mediaCapacity = convertSize(writer.media.capacity, UNIT_SECTORS, UNIT_BYTES) return (writer, mediaCapacity) ######################## # _writeDisc() function ######################## def _writeDisc(config, writer, spanItem): """ Writes a span item to disc. Args: config: Cedar Backup configuration writer: Writer to use spanItem: Span item to write """ print("") _discInitializeImage(config, writer, spanItem) _discWriteImage(config, writer) _discConsistencyCheck(config, writer, spanItem) print("Write process is complete.") print("===") def _discInitializeImage(config, writer, spanItem): """ Initialize an ISO image for a span item. Args: config: Cedar Backup configuration writer: Writer to use spanItem: Span item to write """ complete = False while not complete: try: print("Initializing image...") writer.initializeImage(newDisc=True, tmpdir=config.options.workingDir) for path in spanItem.fileList: graftPoint = os.path.dirname(path.replace(config.store.sourceDir, "", 1)) writer.addImageEntry(path, graftPoint) complete = True except KeyboardInterrupt as e: raise e except Exception as e: logger.error("Failed to initialize image: %s", e) if not _getYesNoAnswer("Retry initialization step?", default="Y"): raise e print("Ok, attempting retry.") print("===") print("Completed initializing image.") # pylint: disable=W0613 def _discWriteImage(config, writer): """ Writes a ISO image for a span item. Args: config: Cedar Backup configuration writer: Writer to use """ complete = False while not complete: try: print("Writing image to disc...") writer.writeImage() complete = True except KeyboardInterrupt as e: raise e except Exception as e: logger.error("Failed to write image: %s", e) if not _getYesNoAnswer("Retry this step?", default="Y"): raise e print("Ok, attempting retry.") _getReturn("Please replace media if needed.\nPress return when ready.") print("===") print("Completed writing image.") def _discConsistencyCheck(config, writer, spanItem): """ Run a consistency check on an ISO image for a span item. Args: config: Cedar Backup configuration writer: Writer to use spanItem: Span item to write """ if config.store.checkData: complete = False while not complete: try: print("Running consistency check...") _consistencyCheck(config, spanItem.fileList) complete = True except KeyboardInterrupt as e: raise e except Exception as e: logger.error("Consistency check failed: %s", e) if not _getYesNoAnswer("Retry the consistency check?", default="Y"): raise e if _getYesNoAnswer("Rewrite the disc first?", default="N"): print("Ok, attempting retry.") _getReturn("Please replace the disc in your backup device.\nPress return when ready.") print("===") _discWriteImage(config, writer) else: print("Ok, attempting retry.") print("===") print("Completed consistency check.") ############################### # _consistencyCheck() function ############################### def _consistencyCheck(config, fileList): """ Runs a consistency check against media in the backup device. The function mounts the device at a temporary mount point in the working directory, and then compares the passed-in file list's digest map with the one generated from the disc. The two lists should be identical. If no exceptions are thrown, there were no problems with the consistency check. @warning: The implementation of this function is very UNIX-specific. Args: config: Config object fileList: BackupFileList whose contents to check against Raises: ValueError: If the check fails IOError: If there is a problem working with the media """ logger.debug("Running consistency check.") mountPoint = tempfile.mkdtemp(dir=config.options.workingDir) try: mount(config.store.devicePath, mountPoint, "iso9660") discList = BackupFileList() discList.addDirContents(mountPoint) sourceList = BackupFileList() sourceList.extend(fileList) discListDigest = discList.generateDigestMap(stripPrefix=normalizeDir(mountPoint)) sourceListDigest = sourceList.generateDigestMap(stripPrefix=normalizeDir(config.store.sourceDir)) compareDigestMaps(sourceListDigest, discListDigest, verbose=True) logger.info("Consistency check completed. No problems found.") finally: unmount(mountPoint, True, 5, 1) # try 5 times, and remove mount point when done ######################################################################### # User interface utilities ######################################################################## def _getYesNoAnswer(prompt, default): """ Get a yes/no answer from the user. The default will be placed at the end of the prompt. A "Y" or "y" is considered yes, anything else no. A blank (empty) response results in the default. Args: prompt: Prompt to show default: Default to set if the result is blank Returns: Boolean true/false corresponding to Y/N """ if default == "Y": prompt = "%s [Y/n]: " % prompt else: prompt = "%s [y/N]: " % prompt answer = input(prompt) if answer in [None, ""]: answer = default if answer[0] in ["Y", "y"]: return True else: return False def _getChoiceAnswer(prompt, default, validChoices): """ Get a particular choice from the user. The default will be placed at the end of the prompt. The function loops until getting a valid choice. A blank (empty) response results in the default. Args: prompt: Prompt to show default: Default to set if the result is None or blank validChoices: List of valid choices (strings) Returns: Valid choice from user """ prompt = "%s [%s]: " % (prompt, default) answer = input(prompt) if answer in [None, ""]: answer = default while answer not in validChoices: print("Choice must be one of %s" % validChoices) answer = input(prompt) return answer def _getFloat(prompt, default): """ Get a floating point number from the user. The default will be placed at the end of the prompt. The function loops until getting a valid floating point number. A blank (empty) response results in the default. Args: prompt: Prompt to show default: Default to set if the result is None or blank Returns: Floating point number from user """ prompt = "%s [%.2f]: " % (prompt, default) while True: answer = input(prompt) if answer in [None, ""]: return default else: try: return float(answer) except ValueError: print("Enter a floating point number.") def _getReturn(prompt): """ Get a return key from the user. Args: prompt: Prompt to show """ input(prompt) ######################################################################### # Main routine ######################################################################## if __name__ == "__main__": sys.exit(cli()) ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/util.py0000644000000000000000000022356314567004737015534 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # Portions copyright (c) 2001, 2002 Python Software Foundation. # All Rights Reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides general-purpose utilities. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides general-purpose utilities. Module Attributes ================= Attributes: ISO_SECTOR_SIZE: Size of an ISO image sector, in bytes BYTES_PER_SECTOR: Number of bytes (B) per ISO sector BYTES_PER_KBYTE: Number of bytes (B) per kilobyte (kB) BYTES_PER_MBYTE: Number of bytes (B) per megabyte (MB) BYTES_PER_GBYTE: Number of bytes (B) per megabyte (GB) KBYTES_PER_MBYTE: Number of kilobytes (kB) per megabyte (MB) MBYTES_PER_GBYTE: Number of megabytes (MB) per gigabyte (GB) SECONDS_PER_MINUTE: Number of seconds per minute MINUTES_PER_HOUR: Number of minutes per hour HOURS_PER_DAY: Number of hours per day SECONDS_PER_DAY: Number of seconds per day UNIT_BYTES: Constant representing the byte (B) unit for conversion UNIT_KBYTES: Constant representing the kilobyte (kB) unit for conversion UNIT_MBYTES: Constant representing the megabyte (MB) unit for conversion UNIT_GBYTES: Constant representing the gigabyte (GB) unit for conversion UNIT_SECTORS: Constant representing the ISO sector unit for conversion :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import collections import logging import math import os import platform import re import sys import time from decimal import Decimal from functools import total_ordering from numbers import Real from subprocess import PIPE, STDOUT, Popen from CedarBackup3.release import VERSION try: import grp import pwd _UID_GID_AVAILABLE = True except ImportError: _UID_GID_AVAILABLE = False ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.util") outputLogger = logging.getLogger("CedarBackup3.output") ISO_SECTOR_SIZE = 2048.0 # in bytes BYTES_PER_SECTOR = ISO_SECTOR_SIZE BYTES_PER_KBYTE = 1024.0 KBYTES_PER_MBYTE = 1024.0 MBYTES_PER_GBYTE = 1024.0 BYTES_PER_MBYTE = BYTES_PER_KBYTE * KBYTES_PER_MBYTE BYTES_PER_GBYTE = BYTES_PER_MBYTE * MBYTES_PER_GBYTE SECONDS_PER_MINUTE = 60.0 MINUTES_PER_HOUR = 60.0 HOURS_PER_DAY = 24.0 SECONDS_PER_DAY = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY UNIT_BYTES = 0 UNIT_KBYTES = 1 UNIT_MBYTES = 2 UNIT_GBYTES = 4 UNIT_SECTORS = 3 MTAB_FILE = "/etc/mtab" MOUNT_COMMAND = ["mount"] UMOUNT_COMMAND = ["umount"] DEFAULT_LANGUAGE = "C" LANG_VAR = "LANG" LOCALE_VARS = [ "LC_ADDRESS", "LC_ALL", "LC_COLLATE", "LC_CTYPE", "LC_IDENTIFICATION", "LC_MEASUREMENT", "LC_MESSAGES", "LC_MONETARY", "LC_NAME", "LC_NUMERIC", "LC_PAPER", "LC_TELEPHONE", "LC_TIME", ] ######################################################################## # UnorderedList class definition ######################################################################## class UnorderedList(list): """ Class representing an "unordered list". An "unordered list" is a list in which only the contents matter, not the order in which the contents appear in the list. For instance, we might be keeping track of set of paths in a list, because it's convenient to have them in that form. However, for comparison purposes, we would only care that the lists contain exactly the same contents, regardless of order. I have come up with two reasonable ways of doing this, plus a couple more that would work but would be a pain to implement. My first method is to copy and sort each list, comparing the sorted versions. This will only work if two lists with exactly the same members are guaranteed to sort in exactly the same order. The second way would be to create two Sets and then compare the sets. However, this would lose information about any duplicates in either list. I've decided to go with option #1 for now. I'll modify this code if I run into problems in the future. We override the original ``__eq__``, ``__ne__``, ``__ge__``, ``__gt__``, ``__le__`` and ``__lt__`` list methods to change the definition of the various comparison operators. In all cases, the comparison is changed to return the result of the original operation *but instead comparing sorted lists*. This is going to be quite a bit slower than a normal list, so you probably only want to use it on small lists. """ def __eq__(self, other): """ Definition of ``==`` operator for this class. Args: other: Other object to compare to Returns: True/false depending on whether ``self == other`` """ if other is None: return False selfSorted = UnorderedList.mixedsort(self[:]) otherSorted = UnorderedList.mixedsort(other[:]) return selfSorted.__eq__(otherSorted) def __ne__(self, other): """ Definition of ``!=`` operator for this class. Args: other: Other object to compare to Returns: True/false depending on whether ``self != other`` """ if other is None: return True selfSorted = UnorderedList.mixedsort(self[:]) otherSorted = UnorderedList.mixedsort(other[:]) return selfSorted.__ne__(otherSorted) def __ge__(self, other): """ Definition of S{>=} operator for this class. Args: other: Other object to compare to Returns: True/false depending on whether ``self >= other`` """ if other is None: return True selfSorted = UnorderedList.mixedsort(self[:]) otherSorted = UnorderedList.mixedsort(other[:]) return selfSorted.__ge__(otherSorted) def __gt__(self, other): """ Definition of ``>`` operator for this class. Args: other: Other object to compare to Returns: True/false depending on whether ``self > other`` """ if other is None: return True selfSorted = UnorderedList.mixedsort(self[:]) otherSorted = UnorderedList.mixedsort(other[:]) return selfSorted.__gt__(otherSorted) def __le__(self, other): """ Definition of S{<=} operator for this class. Args: other: Other object to compare to Returns: True/false depending on whether ``self <= other`` """ if other is None: return False selfSorted = UnorderedList.mixedsort(self[:]) otherSorted = UnorderedList.mixedsort(other[:]) return selfSorted.__le__(otherSorted) def __lt__(self, other): """ Definition of ``<`` operator for this class. Args: other: Other object to compare to Returns: True/false depending on whether ``self < other`` """ if other is None: return False selfSorted = UnorderedList.mixedsort(self[:]) otherSorted = UnorderedList.mixedsort(other[:]) return selfSorted.__lt__(otherSorted) @staticmethod def mixedsort(value): """ Sort a list, making sure we don't blow up if the list happens to include mixed values. @see: http://stackoverflow.com/questions/26575183/how-can-i-get-2-x-like-sorting-behaviour-in-python-3-x """ return sorted(value, key=UnorderedList.mixedkey) @staticmethod def mixedkey(value): """Provide a key for use by mixedsort()""" numeric = Real, Decimal if isinstance(value, numeric): typeinfo = numeric else: typeinfo = type(value) try: # pylint: disable=R0124 x = value < value except TypeError: value = repr(value) return repr(typeinfo), value ######################################################################## # AbsolutePathList class definition ######################################################################## class AbsolutePathList(UnorderedList): """ Class representing a list of absolute paths. This is an unordered list. We override the ``append``, ``insert`` and ``extend`` methods to ensure that any item added to the list is an absolute path. Each item added to the list is encoded using :any:`encodePath`. If we don't do this, we have problems trying certain operations between strings and unicode objects, particularly for "odd" filenames that can't be encoded in standard ASCII. """ def append(self, item): """ Overrides the standard ``append`` method. Raises: ValueError: If item is not an absolute path """ if not os.path.isabs(item): raise ValueError("Not an absolute path: [%s]" % item) list.append(self, encodePath(item)) def insert(self, index, item): """ Overrides the standard ``insert`` method. Raises: ValueError: If item is not an absolute path """ if not os.path.isabs(item): raise ValueError("Not an absolute path: [%s]" % item) list.insert(self, index, encodePath(item)) def extend(self, seq): """ Overrides the standard ``insert`` method. Raises: ValueError: If any item is not an absolute path """ for item in seq: if not os.path.isabs(item): raise ValueError("Not an absolute path: [%s]" % item) for item in seq: list.append(self, encodePath(item)) ######################################################################## # ObjectTypeList class definition ######################################################################## class ObjectTypeList(UnorderedList): """ Class representing a list containing only objects with a certain type. This is an unordered list. We override the ``append``, ``insert`` and ``extend`` methods to ensure that any item added to the list matches the type that is requested. The comparison uses the built-in ``isinstance``, which should allow subclasses of of the requested type to be added to the list as well. The ``objectName`` value will be used in exceptions, i.e. C{"Item must be a CollectDir object."} if ``objectName`` is ``"CollectDir"``. """ def __init__(self, objectType, objectName): """ Initializes a typed list for a particular type. Args: objectType: Type that the list elements must match objectName: Short string containing the "name" of the type """ super(ObjectTypeList, self).__init__() self.objectType = objectType self.objectName = objectName def append(self, item): """ Overrides the standard ``append`` method. Raises: ValueError: If item does not match requested type """ if not isinstance(item, self.objectType): raise ValueError("Item must be a %s object." % self.objectName) list.append(self, item) def insert(self, index, item): """ Overrides the standard ``insert`` method. Raises: ValueError: If item does not match requested type """ if not isinstance(item, self.objectType): raise ValueError("Item must be a %s object." % self.objectName) list.insert(self, index, item) def extend(self, seq): """ Overrides the standard ``insert`` method. Raises: ValueError: If item does not match requested type """ for item in seq: if not isinstance(item, self.objectType): raise ValueError("All items must be %s objects." % self.objectName) list.extend(self, seq) ######################################################################## # RestrictedContentList class definition ######################################################################## class RestrictedContentList(UnorderedList): """ Class representing a list containing only object with certain values. This is an unordered list. We override the ``append``, ``insert`` and ``extend`` methods to ensure that any item added to the list is among the valid values. We use a standard comparison, so pretty much anything can be in the list of valid values. The ``valuesDescr`` value will be used in exceptions, i.e. C{"Item must be one of values in VALID_ACTIONS"} if ``valuesDescr`` is ``"VALID_ACTIONS"``. *Note:* This class doesn't make any attempt to trap for nonsensical arguments. All of the values in the values list should be of the same type (i.e. strings). Then, all list operations also need to be of that type (i.e. you should always insert or append just strings). If you mix types -- for instance lists and strings -- you will likely see AttributeError exceptions or other problems. """ def __init__(self, valuesList, valuesDescr, prefix=None): """ Initializes a list restricted to containing certain values. Args: valuesList: List of valid values valuesDescr: Short string describing list of values prefix: Prefix to use in error messages (None results in prefix "Item") """ super(RestrictedContentList, self).__init__() self.prefix = "Item" if prefix is not None: self.prefix = prefix self.valuesList = valuesList self.valuesDescr = valuesDescr def append(self, item): """ Overrides the standard ``append`` method. Raises: ValueError: If item is not in the values list """ if item not in self.valuesList: raise ValueError("%s must be one of the values in %s." % (self.prefix, self.valuesDescr)) list.append(self, item) def insert(self, index, item): """ Overrides the standard ``insert`` method. Raises: ValueError: If item is not in the values list """ if item not in self.valuesList: raise ValueError("%s must be one of the values in %s." % (self.prefix, self.valuesDescr)) list.insert(self, index, item) def extend(self, seq): """ Overrides the standard ``insert`` method. Raises: ValueError: If item is not in the values list """ for item in seq: if item not in self.valuesList: raise ValueError("%s must be one of the values in %s." % (self.prefix, self.valuesDescr)) list.extend(self, seq) ######################################################################## # RegexMatchList class definition ######################################################################## class RegexMatchList(UnorderedList): """ Class representing a list containing only strings that match a regular expression. If ``emptyAllowed`` is passed in as ``False``, then empty strings are explicitly disallowed, even if they happen to match the regular expression. (``None`` values are always disallowed, since string operations are not permitted on ``None``.) This is an unordered list. We override the ``append``, ``insert`` and ``extend`` methods to ensure that any item added to the list matches the indicated regular expression. *Note:* If you try to put values that are not strings into the list, you will likely get either TypeError or AttributeError exceptions as a result. """ def __init__(self, valuesRegex, emptyAllowed=True, prefix=None): """ Initializes a list restricted to containing certain values. Args: valuesRegex: Regular expression that must be matched, as a string emptyAllowed: Indicates whether empty or None values are allowed prefix: Prefix to use in error messages (None results in prefix "Item") """ super(RegexMatchList, self).__init__() self.prefix = "Item" if prefix is not None: self.prefix = prefix self.valuesRegex = valuesRegex self.emptyAllowed = emptyAllowed self.pattern = re.compile(self.valuesRegex) def append(self, item): """ Overrides the standard ``append`` method. Raises: ValueError: If item is None ValueError: If item is empty and empty values are not allowed ValueError: If item does not match the configured regular expression """ if item is None or (not self.emptyAllowed and item == ""): raise ValueError("%s cannot be empty." % self.prefix) if not self.pattern.search(item): raise ValueError("%s is not valid: [%s]" % (self.prefix, item)) list.append(self, item) def insert(self, index, item): """ Overrides the standard ``insert`` method. Raises: ValueError: If item is None ValueError: If item is empty and empty values are not allowed ValueError: If item does not match the configured regular expression """ if item is None or (not self.emptyAllowed and item == ""): raise ValueError("%s cannot be empty." % self.prefix) if not self.pattern.search(item): raise ValueError("%s is not valid [%s]" % (self.prefix, item)) list.insert(self, index, item) def extend(self, seq): """ Overrides the standard ``insert`` method. Raises: ValueError: If any item is None ValueError: If any item is empty and empty values are not allowed ValueError: If any item does not match the configured regular expression """ for item in seq: if item is None or (not self.emptyAllowed and item == ""): raise ValueError("%s cannot be empty." % self.prefix) if not self.pattern.search(item): raise ValueError("%s is not valid: [%s]" % (self.prefix, item)) list.extend(self, seq) ######################################################################## # RegexList class definition ######################################################################## class RegexList(UnorderedList): """ Class representing a list of valid regular expression strings. This is an unordered list. We override the ``append``, ``insert`` and ``extend`` methods to ensure that any item added to the list is a valid regular expression. """ def append(self, item): """ Overrides the standard ``append`` method. Raises: ValueError: If item is not an absolute path """ try: re.compile(item) except re.error: raise ValueError("Not a valid regular expression: [%s]" % item) list.append(self, item) def insert(self, index, item): """ Overrides the standard ``insert`` method. Raises: ValueError: If item is not an absolute path """ try: re.compile(item) except re.error: raise ValueError("Not a valid regular expression: [%s]" % item) list.insert(self, index, item) def extend(self, seq): """ Overrides the standard ``insert`` method. Raises: ValueError: If any item is not an absolute path """ for item in seq: try: re.compile(item) except re.error: raise ValueError("Not a valid regular expression: [%s]" % item) for item in seq: list.append(self, item) ######################################################################## # Directed graph implementation ######################################################################## class _Vertex(object): """ Represents a vertex (or node) in a directed graph. """ def __init__(self, name): """ Constructor. Args: name (String value): Name of this graph vertex """ self.name = name self.endpoints = [] self.state = None @total_ordering class DirectedGraph(object): """ Represents a directed graph. A graph **G=(V,E)** consists of a set of vertices **V** together with a set **E** of vertex pairs or edges. In a directed graph, each edge also has an associated direction (from vertext **v1** to vertex **v2**). A ``DirectedGraph`` object provides a way to construct a directed graph and execute a depth- first search. This data structure was designed based on the graphing chapter in U{The Algorithm Design Manual}, by Steven S. Skiena. This class is intended to be used by Cedar Backup for dependency ordering. Because of this, it's not quite general-purpose. Unlike a "general" graph, every vertex in this graph has at least one edge pointing to it, from a special "start" vertex. This is so no vertices get "lost" either because they have no dependencies or because nothing depends on them. """ _UNDISCOVERED = 0 _DISCOVERED = 1 _EXPLORED = 2 def __init__(self, name): """ Directed graph constructor. Args: name (String value): Name of this graph """ if name is None or name == "": raise ValueError("Graph name must be non-empty.") self._name = name self._vertices = {} self._startVertex = _Vertex(None) # start vertex is only vertex with no name def __repr__(self): """ Official string representation for class instance. """ return "DirectedGraph(%s)" % self.name def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def __eq__(self, other): """Equals operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) == 0 def __lt__(self, other): """Less-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) < 0 def __gt__(self, other): """Greater-than operator, implemented in terms of original Python 2 compare operator.""" return self.__cmp__(other) > 0 def __cmp__(self, other): """ Original Python 2 comparison operator. Args: other: Other object to compare to Returns: -1/0/1 depending on whether self is ``<``, ``=`` or ``>`` other """ # pylint: disable=W0212 if other is None: return 1 if self.name != other.name: if str(self.name or "") < str(other.name or ""): return -1 else: return 1 if self._vertices != other._vertices: if self._vertices < other._vertices: return -1 else: return 1 return 0 def _getName(self): """ Property target used to get the graph name. """ return self._name name = property(_getName, None, None, "Name of the graph.") def createVertex(self, name): """ Creates a named vertex. Args: name: vertex name Raises: ValueError: If the vertex name is ``None`` or empty """ if name is None or name == "": raise ValueError("Vertex name must be non-empty.") vertex = _Vertex(name) self._startVertex.endpoints.append(vertex) # so every vertex is connected at least once self._vertices[name] = vertex def createEdge(self, start, finish): """ Adds an edge with an associated direction, from ``start`` vertex to ``finish`` vertex. Args: start: Name of start vertex finish: Name of finish vertex Raises: ValueError: If one of the named vertices is unknown """ try: startVertex = self._vertices[start] finishVertex = self._vertices[finish] startVertex.endpoints.append(finishVertex) except KeyError as e: raise ValueError("Vertex [%s] could not be found." % e) def topologicalSort(self): """ Implements a topological sort of the graph. This method also enforces that the graph is a directed acyclic graph, which is a requirement of a topological sort. A directed acyclic graph (or "DAG") is a directed graph with no directed cycles. A topological sort of a DAG is an ordering on the vertices such that all edges go from left to right. Only an acyclic graph can have a topological sort, but any DAG has at least one topological sort. Since a topological sort only makes sense for an acyclic graph, this method throws an exception if a cycle is found. A depth-first search only makes sense if the graph is acyclic. If the graph contains any cycles, it is not possible to determine a consistent ordering for the vertices. *Note:* If a particular vertex has no edges, then its position in the final list depends on the order in which the vertices were created in the graph. If you're using this method to determine a dependency order, this makes sense: a vertex with no dependencies can go anywhere (and will). Returns: Ordering on the vertices so that all edges go from left to right Raises: ValueError: If a cycle is found in the graph """ ordering = [] for key in self._vertices: vertex = self._vertices[key] vertex.state = self._UNDISCOVERED for key in self._vertices: vertex = self._vertices[key] if vertex.state == self._UNDISCOVERED: self._topologicalSort(self._startVertex, ordering) return ordering def _topologicalSort(self, vertex, ordering): """ Recursive depth first search function implementing topological sort. Args: vertex: Vertex to search ordering: List of vertices in proper order """ vertex.state = self._DISCOVERED for endpoint in vertex.endpoints: if endpoint.state == self._UNDISCOVERED: self._topologicalSort(endpoint, ordering) elif endpoint.state != self._EXPLORED: raise ValueError("Cycle found in graph (found '%s' while searching '%s')." % (vertex.name, endpoint.name)) if vertex.name is not None: ordering.insert(0, vertex.name) vertex.state = self._EXPLORED ######################################################################## # PathResolverSingleton class definition ######################################################################## class PathResolverSingleton(object): """ Singleton used for resolving executable paths. Various functions throughout Cedar Backup (including extensions) need a way to resolve the path of executables that they use. For instance, the image functionality needs to find the ``mkisofs`` executable, and the Subversion extension needs to find the ``svnlook`` executable. Cedar Backup's original behavior was to assume that the simple name (``"svnlook"`` or whatever) was available on the caller's ``$PATH``, and to fail otherwise. However, this turns out to be less than ideal, since for instance the root user might not always have executables like ``svnlook`` in its path. One solution is to specify a path (either via an absolute path or some sort of path insertion or path appending mechanism) that would apply to the ``executeCommand()`` function. This is not difficult to implement, but it seem like kind of a "big hammer" solution. Besides that, it might also represent a security flaw (for instance, I prefer not to mess with root's ``$PATH`` on the application level if I don't have to). The alternative is to set up some sort of configuration for the path to certain executables, i.e. "find ``svnlook`` in ``/usr/local/bin/svnlook``" or whatever. This PathResolverSingleton aims to provide a good solution to the mapping problem. Callers of all sorts (extensions or not) can get an instance of the singleton. Then, they call the ``lookup`` method to try and resolve the executable they are looking for. Through the ``lookup`` method, the caller can also specify a default to use if a mapping is not found. This way, with no real effort on the part of the caller, behavior can neatly degrade to something equivalent to the current behavior if there is no special mapping or if the singleton was never initialized in the first place. Even better, extensions automagically get access to the same resolver functionality, and they don't even need to understand how the mapping happens. All extension authors need to do is document what executables their code requires, and the standard resolver configuration section will meet their needs. The class should be initialized once through the constructor somewhere in the main routine. Then, the main routine should call the :any:`fill` method to fill in the resolver's internal structures. Everyone else who needs to resolve a path will get an instance of the class using :any:`getInstance` and will then just call the :any:`lookup` method. Attributes: _instance: Holds a reference to the singleton _mapping: Internal mapping from resource name to path """ _instance = None # Holds a reference to singleton instance class _Helper: """Helper class to provide a singleton factory method.""" def __init__(self): pass def __call__(self, *args, **kw): # pylint: disable=W0212 if PathResolverSingleton._instance is None: obj = PathResolverSingleton() PathResolverSingleton._instance = obj return PathResolverSingleton._instance getInstance = _Helper() # Method that callers will use to get an instance def __init__(self): """Singleton constructor, which just creates the singleton instance.""" PathResolverSingleton._instance = self self._mapping = {} def lookup(self, name, default=None): """ Looks up name and returns the resolved path associated with the name. Args: name: Name of the path resource to resolve default: Default to return if resource cannot be resolved Returns: Resolved path associated with name, or default if name can't be resolved """ value = default if name in list(self._mapping.keys()): value = self._mapping[name] logger.debug("Resolved command [%s] to [%s].", name, value) return value def fill(self, mapping): """ Fills in the singleton's internal mapping from name to resource. Args: mapping (Dictionary mapping name to path, both as strings): Mapping from resource name to path """ self._mapping = {} for key in list(mapping.keys()): self._mapping[key] = mapping[key] ######################################################################## # Pipe class definition ######################################################################## class Pipe(Popen): """ Specialized pipe class for use by ``executeCommand``. The :any:`executeCommand` function needs a specialized way of interacting with a pipe. First, ``executeCommand`` only reads from the pipe, and never writes to it. Second, ``executeCommand`` needs a way to discard all output written to ``stderr``, as a means of simulating the shell ``2>/dev/null`` construct. """ # noinspection PyArgumentList def __init__(self, cmd, bufsize=-1, ignoreStderr=False): stderr = STDOUT if ignoreStderr: devnull = nullDevice() stderr = os.open(devnull, os.O_RDWR) Popen.__init__(self, shell=False, args=cmd, bufsize=bufsize, stdin=None, stdout=PIPE, stderr=stderr) ######################################################################## # Diagnostics class definition ######################################################################## class Diagnostics(object): """ Class holding runtime diagnostic information. Diagnostic information is information that is useful to get from users for debugging purposes. I'm consolidating it all here into one object. """ def __init__(self): """ Constructor for the ``Diagnostics`` class. """ def __repr__(self): """ Official string representation for class instance. """ return "Diagnostics()" def __str__(self): """ Informal string representation for class instance. """ return self.__repr__() def getValues(self): """ Get a map containing all of the diagnostic values. Returns: Map from diagnostic name to diagnostic value """ values = {} values["version"] = self.version values["interpreter"] = self.interpreter values["platform"] = self.platform values["encoding"] = self.encoding values["locale"] = self.locale values["timestamp"] = self.timestamp return values def printDiagnostics(self, fd=sys.stdout, prefix=""): """ Pretty-print diagnostic information to a file descriptor. Args: fd: File descriptor used to print information prefix: Prefix string (if any) to place onto printed lines *Note:* The ``fd`` is used rather than ``print`` to facilitate unit testing. """ lines = self._buildDiagnosticLines(prefix) for line in lines: fd.write("%s\n" % line) def logDiagnostics(self, method, prefix=""): """ Pretty-print diagnostic information using a logger method. Args: method: Logger method to use for logging (i.e. logger.info) prefix: Prefix string (if any) to place onto printed lines """ lines = self._buildDiagnosticLines(prefix) for line in lines: method("%s" % line) def _buildDiagnosticLines(self, prefix=""): """ Build a set of pretty-printed diagnostic lines. Args: prefix: Prefix string (if any) to place onto printed lines Returns: List of strings, not terminated by newlines """ values = self.getValues() keys = list(values.keys()) keys.sort() tmax = Diagnostics._getMaxLength(keys) + 3 # three extra dots in output lines = [] for key in keys: title = key.title() title += (tmax - len(title)) * "." value = values[key] line = "%s%s: %s" % (prefix, title, value) lines.append(line) return lines @staticmethod def _getMaxLength(values): """ Get the maximum length from among a list of strings. """ tmax = 0 for value in values: if len(value) > tmax: tmax = len(value) return tmax def _getVersion(self): """ Property target to get the Cedar Backup version. """ return "Cedar Backup %s" % VERSION def _getInterpreter(self): """ Property target to get the Python interpreter version. """ version = sys.version_info return "Python %d.%d.%d (%s)" % (version[0], version[1], version[2], version[3]) def _getEncoding(self): """ Property target to get the filesystem encoding. """ return sys.getfilesystemencoding() or sys.getdefaultencoding() def _getPlatform(self): """ Property target to get the operating system platform. """ try: if sys.platform == "win32": sysname = "win32" release = platform.platform() machine = platform.machine() return "%s (%s %s)" % (sysname, release, machine) else: uname = os.uname() # pylint: disable=no-member sysname = uname[0] # i.e. Linux release = uname[2] # i.e. 2.16.18-2 machine = uname[4] # i.e. i686 return "%s (%s %s %s)" % (sys.platform, sysname, release, machine) except: return sys.platform def _getLocale(self): """ Property target to get the default locale that is in effect. """ try: import locale # pylint: disable=import-outside-toplevel try: return locale.getlocale()[0] # python >= 3.11 deprecates getdefaultlocale() in favor of getlocale() except: return locale.getdefaultlocale()[0] # pylint: disable=deprecated-method: except: return "(unknown)" def _getTimestamp(self): """ Property target to get a current date/time stamp. """ try: import datetime # pylint: disable=import-outside-toplevel if list(map(int, [sys.version_info[0], sys.version_info[1]])) < [3, 12]: # Starting with Python 3.12, utcnow() is deprecated return datetime.datetime.utcnow().ctime() + " UTC" else: return datetime.datetime.now(datetime.UTC).ctime() + " UTC" # pylint: disable=no-member: except: return "(unknown)" version = property(_getVersion, None, None, "Cedar Backup version.") interpreter = property(_getInterpreter, None, None, "Python interpreter version.") platform = property(_getPlatform, None, None, "Platform identifying information.") encoding = property(_getEncoding, None, None, "Filesystem encoding that is in effect.") locale = property(_getLocale, None, None, "Locale that is in effect.") timestamp = property(_getTimestamp, None, None, "Current timestamp.") ######################################################################## # General utility functions ######################################################################## ###################### # sortDict() function ###################### def sortDict(d): """ Returns the keys of the dictionary sorted by value. Args: d: Dictionary to operate on Returns: List of dictionary keys sorted in order by dictionary value """ items = list(d.items()) items.sort(key=lambda x: (x[1], x[0])) # sort by value and then by key return [key for key, value in items] ######################## # removeKeys() function ######################## def removeKeys(d, keys): """ Removes all of the keys from the dictionary. The dictionary is altered in-place. Each key must exist in the dictionary. Args: d: Dictionary to operate on keys: List of keys to remove Raises: KeyError: If one of the keys does not exist """ for key in keys: del d[key] ######################### # convertSize() function ######################### def convertSize(size, fromUnit, toUnit): """ Converts a size in one unit to a size in another unit. This is just a convenience function so that the functionality can be implemented in just one place. Internally, we convert values to bytes and then to the final unit. The available units are: - ``UNIT_BYTES`` - Bytes - ``UNIT_KBYTES`` - Kilobytes, where 1 kB = 1024 B - ``UNIT_MBYTES`` - Megabytes, where 1 MB = 1024 kB - ``UNIT_GBYTES`` - Gigabytes, where 1 GB = 1024 MB - ``UNIT_SECTORS`` - Sectors, where 1 sector = 2048 B Args: size (Integer or float value in units of ``fromUnit``): Size to convert fromUnit (One of the units listed above): Unit to convert from toUnit (One of the units listed above): Unit to convert to Returns: Number converted to new unit, as a float Raises: ValueError: If one of the units is invalid """ if size is None: raise ValueError("Cannot convert size of None.") if fromUnit == UNIT_BYTES: byteSize = float(size) elif fromUnit == UNIT_KBYTES: byteSize = float(size) * BYTES_PER_KBYTE elif fromUnit == UNIT_MBYTES: byteSize = float(size) * BYTES_PER_MBYTE elif fromUnit == UNIT_GBYTES: byteSize = float(size) * BYTES_PER_GBYTE elif fromUnit == UNIT_SECTORS: byteSize = float(size) * BYTES_PER_SECTOR else: raise ValueError("Unknown 'from' unit %s." % fromUnit) if toUnit == UNIT_BYTES: return byteSize elif toUnit == UNIT_KBYTES: return byteSize / BYTES_PER_KBYTE elif toUnit == UNIT_MBYTES: return byteSize / BYTES_PER_MBYTE elif toUnit == UNIT_GBYTES: return byteSize / BYTES_PER_GBYTE elif toUnit == UNIT_SECTORS: return byteSize / BYTES_PER_SECTOR else: raise ValueError("Unknown 'to' unit %s." % toUnit) ########################## # displayBytes() function ########################## def displayBytes(bytes, digits=2): # pylint: disable=W0622 """ Format a byte quantity so it can be sensibly displayed. It's rather difficult to look at a number like "72372224 bytes" and get any meaningful information out of it. It would be more useful to see something like "69.02 MB". That's what this function does. Any time you want to display a byte value, i.e.:: print "Size: %s bytes" % bytes Call this function instead:: print "Size: %s" % displayBytes(bytes) What comes out will be sensibly formatted. The indicated number of digits will be listed after the decimal point, rounded based on whatever rules are used by Python's standard ``%f`` string format specifier. (Values less than 1 kB will be listed in bytes and will not have a decimal point, since the concept of a fractional byte is nonsensical.) Args: bytes (Integer number of bytes): Byte quantity digits (Integer value, typically 2-5): Number of digits to display after the decimal point Returns: String, formatted for sensible display """ if bytes is None: raise ValueError("Cannot display byte value of None.") bytes = float(bytes) if math.fabs(bytes) < BYTES_PER_KBYTE: fmt = "%.0f bytes" value = bytes elif math.fabs(bytes) < BYTES_PER_MBYTE: fmt = "%." + "%d" % digits + "f kB" value = bytes / BYTES_PER_KBYTE elif math.fabs(bytes) < BYTES_PER_GBYTE: fmt = "%." + "%d" % digits + "f MB" value = bytes / BYTES_PER_MBYTE else: fmt = "%." + "%d" % digits + "f GB" value = bytes / BYTES_PER_GBYTE return fmt % value ################################## # getFunctionReference() function ################################## def getFunctionReference(module, function): """ Gets a reference to a named function. This does some hokey-pokey to get back a reference to a dynamically named function. For instance, say you wanted to get a reference to the ``os.path.isdir`` function. You could use:: myfunc = getFunctionReference("os.path", "isdir") Although we won't bomb out directly, behavior is pretty much undefined if you pass in ``None`` or ``""`` for either ``module`` or ``function``. The only validation we enforce is that whatever we get back must be callable. I derived this code based on the internals of the Python unittest implementation. I don't claim to completely understand how it works. Args: module (Something like "os.path" or "CedarBackup3.util"): Name of module associated with function function (Something like "isdir" or "getUidGid"): Name of function Returns: Reference to function associated with name Raises: ImportError: If the function cannot be found ValueError: If the resulting reference is not callable @copyright: Some of this code, prior to customization, was originally part of the Python 2.3 codebase. Python code is copyright (c) 2001, 2002 Python Software Foundation; All Rights Reserved. """ parts = [] if module is not None and module != "": parts = module.split(".") if function is not None and function != "": parts.append(function) copy = parts[:] while copy: try: module = __import__(".".join(copy)) break except ImportError: del copy[-1] if not copy: raise parts = parts[1:] obj = module for part in parts: obj = getattr(obj, part) if not isinstance(obj, collections.abc.Callable): raise ValueError("Reference to %s.%s is not callable." % (module, function)) return obj ####################### # getUidGid() function ####################### def getUidGid(user, group): """ Get the uid/gid associated with a user/group pair This is a no-op if user/group functionality is not available on the platform. Args: user (User name as a string): User name group (Group name as a string): Group name Returns: Tuple ``(uid, gid)`` matching passed-in user and group Raises: ValueError: If the ownership user/group values are invalid """ if _UID_GID_AVAILABLE: try: uid = pwd.getpwnam(user)[2] gid = grp.getgrnam(group)[2] return (uid, gid) except Exception as e: logger.debug("Error looking up uid and gid for [%s:%s]: %s", user, group, e) raise ValueError("Unable to lookup up uid and gid for passed in user/group.") else: return (0, 0) ############################# # changeOwnership() function ############################# def changeOwnership(path, user, group): """ Changes ownership of path to match the user and group. This is a no-op if user/group functionality is not available on the platform, or if the either passed-in user or group is ``None``. Further, we won't even try to do it unless running as root, since it's unlikely to work. Args: path: Path whose ownership to change user: User which owns file group: Group which owns file """ if _UID_GID_AVAILABLE: if sys.platform == "win32": logger.debug("Chown not supported on Windows platform") elif user is None or group is None: logger.debug("User or group is None, so not attempting to change owner on [%s].", path) elif not isRunningAsRoot(): logger.debug("Not root, so not attempting to change owner on [%s].", path) else: try: (uid, gid) = getUidGid(user, group) os.chown(path, uid, gid) # pylint: disable=no-member except Exception as e: logger.error("Error changing ownership of [%s]: %s", path, e) ############################# # isRunningAsRoot() function ############################# def isRunningAsRoot(): """ Indicates whether the program is running as the root user. """ if sys.platform == "win32": return False return os.getuid() == 0 # pylint: disable=no-member ############################## # splitCommandLine() function ############################## def splitCommandLine(commandLine): """ Splits a command line string into a list of arguments. Unfortunately, there is no "standard" way to parse a command line string, and it's actually not an easy problem to solve portably (essentially, we have to emulate the shell argument-processing logic). This code only respects double quotes (``"``) for grouping arguments, not single quotes (``'``). Make sure you take this into account when building your command line. Incidentally, I found this particular parsing method while digging around in Google Groups, and I tweaked it for my own use. Args: commandLine (String, i.e. "cback3 --verbose stage store"): Command line string Returns: List of arguments, suitable for passing to ``popen2`` Raises: ValueError: If the command line is None """ if commandLine is None: raise ValueError("Cannot split command line of None.") fields = re.findall('[^ "]+|"[^"]+"', commandLine) fields = [field.replace('"', "") for field in fields] return fields ############################ # resolveCommand() function ############################ def resolveCommand(command): """ Resolves the real path to a command through the path resolver mechanism. Both extensions and standard Cedar Backup functionality need a way to resolve the "real" location of various executables. Normally, they assume that these executables are on the system path, but some callers need to specify an alternate location. Ideally, we want to handle this configuration in a central location. The Cedar Backup path resolver mechanism (a singleton called :any:`PathResolverSingleton`) provides the central location to store the mappings. This function wraps access to the singleton, and is what all functions (extensions or standard functionality) should call if they need to find a command. The passed-in command must actually be a list, in the standard form used by all existing Cedar Backup code (something like ``["svnlook", ]``). The lookup will actually be done on the first element in the list, and the returned command will always be in list form as well. If the passed-in command can't be resolved or no mapping exists, then the command itself will be returned unchanged. This way, we neatly fall back on default behavior if we have no sensible alternative. Args: command (List form of command, i.e. ``["svnlook", ]``): Command to resolve Returns: Path to command or just command itself if no mapping exists """ singleton = PathResolverSingleton.getInstance() name = command[0] result = command[:] result[0] = singleton.lookup(name, name) return result ############################ # executeCommand() function ############################ def executeCommand(command, args, returnOutput=False, ignoreStderr=False, doNotLog=False, outputFile=None): """ Executes a shell command, hopefully in a safe way. This function exists to replace direct calls to ``os.popen`` in the Cedar Backup code. It's not safe to call a function such as ``os.popen()`` with untrusted arguments, since that can cause problems if the string contains non-safe variables or other constructs (imagine that the argument is ``$WHATEVER``, but ``$WHATEVER`` contains something like C{"; rm -fR ~/; echo"} in the current environment). Instead, it's safer to pass a list of arguments in the style supported bt ``popen2`` or ``popen4``. This function actually uses a specialized ``Pipe`` class implemented using either ``subprocess.Popen`` or ``popen2.Popen4``. Under the normal case, this function will return a tuple of C{(status, None)} where the status is the wait-encoded return status of the call per the ``popen2.Popen4`` documentation. If ``returnOutput`` is passed in as ``True``, the function will return a tuple of ``(status, output)`` where ``output`` is a list of strings, one entry per line in the output from the command. Output is always logged to the ``outputLogger.info()`` target, regardless of whether it's returned. By default, ``stdout`` and ``stderr`` will be intermingled in the output. However, if you pass in ``ignoreStderr=True``, then only ``stdout`` will be included in the output. The ``doNotLog`` parameter exists so that callers can force the function to not log command output to the debug log. Normally, you would want to log. However, if you're using this function to write huge output files (i.e. database backups written to ``stdout``) then you might want to avoid putting all that information into the debug log. The ``outputFile`` parameter exists to make it easier for a caller to push output into a file, i.e. as a substitute for redirection to a file. If this value is passed in, each time a line of output is generated, it will be written to the file using ``outputFile.write()``. At the end, the file descriptor will be flushed using ``outputFile.flush()``. The caller maintains responsibility for closing the file object appropriately. *Note:* I know that it's a bit confusing that the command and the arguments are both lists. I could have just required the caller to pass in one big list. However, I think it makes some sense to keep the command (the constant part of what we're executing, i.e. ``"scp -B"``) separate from its arguments, even if they both end up looking kind of similar. *Note:* You cannot redirect output via shell constructs (i.e. ``>file``, ``2>/dev/null``, etc.) using this function. The redirection string would be passed to the command just like any other argument. However, you can implement the equivalent to redirection using ``ignoreStderr`` and ``outputFile``, as discussed above. *Note:* The operating system environment is partially sanitized before the command is invoked. See :any:`sanitizeEnvironment` for details. Args: command (List of individual arguments that make up the command): Shell command to execute args (List of additional arguments to the command): List of arguments to the command returnOutput (Boolean ``True`` or ``False``): Indicates whether to return the output of the command ignoreStderr (Boolean True or False): Whether stderr should be discarded doNotLog (Boolean ``True`` or ``False``): Indicates that output should not be logged outputFile (File as from ``open`` or ``file``, binary write): File that all output should be written to Returns: Tuple of ``(result, output)`` as described above """ # I refactored this in Oct 2020 when modernizing the packaging. Recent versions of # Python gave a "ResourceWarning: unclosed file <_io.BufferedReader name=72>". From # StackOverflow (https://stackoverflow.com/a/58696973/2907667), I decided that the solution # was to use the Pipe as a context manager, which helps ensure that all of its associated # resources are cleaned up properly. However, the error handling below is somewhat # complicated, for reasons I am sure were important in 2004 but are not clear to me now. # The error conditions are also hard to test. So, there's a chance that my refactoring is # not strictly equivalent to the original code. If needed, the original code can be # found at this commit: # https://github.com/pronovic/cedar-backup3/blob/370dbc9ea2b7a5ad9533605ead32d1e56746efd4/CedarBackup3/util.py#L1449 logger.debug("Executing command %s with args %s.", command, args) outputLogger.info("Executing command %s with args %s.", command, args) if doNotLog: logger.debug("Note: output will not be logged, per the doNotLog flag.") outputLogger.info("Note: output will not be logged, per the doNotLog flag.") output = [] fields = command[:] # make sure to copy it so we don't destroy it fields.extend(args) try: sanitizeEnvironment() # make sure we have a consistent environment with Pipe(fields, ignoreStderr=ignoreStderr) as pipe: try: while True: line = pipe.stdout.readline() if not line: break if returnOutput: output.append(line.decode("utf-8")) if outputFile is not None: outputFile.write(line) if not doNotLog: outputLogger.info(line.decode("utf-8")[:-1]) # this way the log will (hopefully) get updated in realtime if outputFile is not None: try: # note, not every file-like object can be flushed outputFile.flush() except: pass if returnOutput: return (pipe.wait(), output) else: return (pipe.wait(), None) except OSError as e: logger.debug("Command returned OSError: %s", e) if returnOutput: if output: return (pipe.wait(), output) else: return (pipe.wait(), [e]) else: return (pipe.wait(), None) except OSError as e: logger.debug("Command returned OSError: %s", e) if returnOutput: return (256, output) else: return (256, None) ############################## # calculateFileAge() function ############################## def calculateFileAge(path): """ Calculates the age (in days) of a file. The "age" of a file is the amount of time since the file was last used, per the most recent of the file's ``st_atime`` and ``st_mtime`` values. Technically, we only intend this function to work with files, but it will probably work with anything on the filesystem. Args: path: Path to a file on disk Returns: Age of the file in days (possibly fractional) Raises: OSError: If the file doesn't exist """ currentTime = int(time.time()) fileStats = os.stat(path) lastUse = max(fileStats.st_atime, fileStats.st_mtime) # "most recent" is "largest" ageInSeconds = currentTime - lastUse ageInDays = ageInSeconds / SECONDS_PER_DAY return ageInDays ################### # mount() function ################### def mount(devicePath, mountPoint, fsType): """ Mounts the indicated device at the indicated mount point. For instance, to mount a CD, you might use device path ``/dev/cdrw``, mount point ``/media/cdrw`` and filesystem type ``iso9660``. You can safely use any filesystem type that is supported by ``mount`` on your platform. If the type is ``None``, we'll attempt to let ``mount`` auto-detect it. This may or may not work on all systems. *Note:* This only works on platforms that have a concept of "mounting" a filesystem through a command-line ``"mount"`` command, like UNIXes. It won't work on Windows. Args: devicePath: Path of device to be mounted mountPoint: Path that device should be mounted at fsType: Type of the filesystem assumed to be available via the device Raises: IOError: If the device cannot be mounted """ if fsType is None: args = [devicePath, mountPoint] else: args = ["-t", fsType, devicePath, mountPoint] command = resolveCommand(MOUNT_COMMAND) result = executeCommand(command, args, returnOutput=False, ignoreStderr=True)[0] if result != 0: raise IOError("Error [%d] mounting [%s] at [%s] as [%s]." % (result, devicePath, mountPoint, fsType)) ##################### # unmount() function ##################### def unmount(mountPoint, removeAfter=False, attempts=1, waitSeconds=0): """ Unmounts whatever device is mounted at the indicated mount point. Sometimes, it might not be possible to unmount the mount point immediately, if there are still files open there. Use the ``attempts`` and ``waitSeconds`` arguments to indicate how many unmount attempts to make and how many seconds to wait between attempts. If you pass in zero attempts, no attempts will be made (duh). If the indicated mount point is not really a mount point per ``os.path.ismount()``, then it will be ignored. This seems to be a safer check then looking through ``/etc/mtab``, since ``ismount()`` is already in the Python standard library and is documented as working on all POSIX systems. If ``removeAfter`` is ``True``, then the mount point will be removed using ``os.rmdir()`` after the unmount action succeeds. If for some reason the mount point is not a directory, then it will not be removed. *Note:* This only works on platforms that have a concept of "mounting" a filesystem through a command-line ``"mount"`` command, like UNIXes. It won't work on Windows. Args: mountPoint: Mount point to be unmounted removeAfter: Remove the mount point after unmounting it attempts: Number of times to attempt the unmount waitSeconds: Number of seconds to wait between repeated attempts Raises: IOError: If the mount point is still mounted after attempts are exhausted """ if os.path.ismount(mountPoint): for attempt in range(0, attempts): logger.debug("Making attempt %d to unmount [%s].", attempt, mountPoint) command = resolveCommand(UMOUNT_COMMAND) result = executeCommand(command, [mountPoint], returnOutput=False, ignoreStderr=True)[0] if result != 0: logger.error("Error [%d] unmounting [%s] on attempt %d.", result, mountPoint, attempt) elif os.path.ismount(mountPoint): logger.error("After attempt %d, [%s] is still mounted.", attempt, mountPoint) else: logger.debug("Successfully unmounted [%s] on attempt %d.", mountPoint, attempt) break # this will cause us to skip the loop else: clause if attempt + 1 < attempts: # i.e. this isn't the last attempt if waitSeconds > 0: logger.info("Sleeping %d second(s) before next unmount attempt.", waitSeconds) time.sleep(waitSeconds) else: if os.path.ismount(mountPoint): raise IOError("Unable to unmount [%s] after %d attempts." % (mountPoint, attempts)) logger.info("Mount point [%s] seems to have finally gone away.", mountPoint) if os.path.isdir(mountPoint) and removeAfter: logger.debug("Removing mount point [%s].", mountPoint) os.rmdir(mountPoint) ########################### # deviceMounted() function ########################### def deviceMounted(devicePath): """ Indicates whether a specific filesystem device is currently mounted. We determine whether the device is mounted by looking through the system's ``mtab`` file. This file shows every currently-mounted filesystem, ordered by device. We only do the check if the ``mtab`` file exists and is readable. Otherwise, we assume that the device is not mounted. *Note:* This only works on platforms that have a concept of an mtab file to show mounted volumes, like UNIXes. It won't work on Windows. Args: devicePath: Path of device to be checked Returns: True if device is mounted, false otherwise """ if os.path.exists(MTAB_FILE) and os.access(MTAB_FILE, os.R_OK): realPath = os.path.realpath(devicePath) with open(MTAB_FILE) as f: # pylint: disable=unspecified-encoding lines = f.readlines() for line in lines: (mountDevice, mountPoint, remainder) = line.split(None, 2) if mountDevice in [devicePath, realPath]: logger.debug("Device [%s] is mounted at [%s].", devicePath, mountPoint) return True return False ######################## # encodePath() function ######################## def encodePath(path): """ Safely encodes a filesystem path as a Unicode string, converting bytes to fileystem encoding if necessary. Args: path: Path to encode Returns: Path, as a string, encoded appropriately Raises: ValueError: If the path cannot be encoded properly @see: http://lucumr.pocoo.org/2013/7/2/the-updated-guide-to-unicode/ """ if path is None: return path try: if isinstance(path, bytes): encoding = sys.getfilesystemencoding() or sys.getdefaultencoding() path = path.decode(encoding, "surrogateescape") # to match what os.listdir() does return path except UnicodeError as e: raise ValueError("Path could not be safely encoded as %s: %s" % (encoding, str(e))) ###################### # pathJoin() function ###################### def pathJoin(path, *paths): """ Wraps os.path.join(), normalizing slashes in the result. On Windows in particular, we often end up with mixed slashes, where parts of a path have forward slash and parts have backward slash. This makes it difficult to construct exclusions in configuration, because you never know what part of a path will have what kind of slash. I've decided to standardize on forward slashes. Returns: Result as from os.path.join() but always with forward slashes. """ result = os.path.join(path, *paths) return result.replace("\\", "/") ######################## # nullDevice() function ######################## def nullDevice(): """ Attempts to portably return the null device on this system. The null device is something like ``/dev/null`` on a UNIX system. The name varies on other platforms. """ return os.devnull ############################## # deriveDayOfWeek() function ############################## def deriveDayOfWeek(dayName): """ Converts English day name to numeric day of week as from ``time.localtime``. For instance, the day ``monday`` would be converted to the number ``0``. Args: dayName (string, i.e. ``"monday"``, ``"tuesday"``, etc): Day of week to convert Returns: Integer, where Monday is 0 and Sunday is 6; or -1 if no conversion is possible """ if dayName.lower() == "monday": return 0 elif dayName.lower() == "tuesday": return 1 elif dayName.lower() == "wednesday": return 2 elif dayName.lower() == "thursday": return 3 elif dayName.lower() == "friday": return 4 elif dayName.lower() == "saturday": return 5 elif dayName.lower() == "sunday": return 6 else: return -1 # What else can we do?? Thrown an exception, I guess. ########################### # isStartOfWeek() function ########################### def isStartOfWeek(startingDay): """ Indicates whether "today" is the backup starting day per configuration. If the current day's English name matches the indicated starting day, then today is a starting day. Args: startingDay (string, i.e. ``"monday"``, ``"tuesday"``, etc): Configured starting day Returns: Boolean indicating whether today is the starting day """ value = time.localtime().tm_wday == deriveDayOfWeek(startingDay) if value: logger.debug("Today is the start of the week.") else: logger.debug("Today is NOT the start of the week.") return value ################################# # buildNormalizedPath() function ################################# def buildNormalizedPath(path): """ Returns a "normalized" path based on a path name. A normalized path is a representation of a path that is also a valid file name. To make a valid file name out of a complete path, we have to convert or remove some characters that are significant to the filesystem -- in particular, the path separator, the Windows drive separator, and any leading ``'.'`` character (which would cause the file to be hidden in a file listing). Note that this is a one-way transformation -- you can't safely derive the original path from the normalized path. To normalize a path, we begin by looking at the first character. If the first character is ``'/'`` or ``'\\'``, it gets removed. If the first character is ``'.'``, it gets converted to ``'_'``. Then, we look through the rest of the path and convert all remaining ``'/'`` or ``'\\'`` characters ``'-'``, and all remaining whitespace or ``':'`` characters to ``'_'``. As a special case, a path consisting only of a single ``'/'`` or ``'\\'`` character will be converted to ``'_'``. That's better than ``'-'``, because a dash tends to get interpreted by shell commands as a switch. As a special case, anything that looks like a leading Windows drive letter combination (like ``c:\\`` or ``c:/``) will be converted to something like ``c-``. This is needed because a colon isn't a valid filename character on Windows. Args: path: Path to normalize Returns: Normalized path as described above Raises: ValueError: If the path is None """ if path is None: raise ValueError("Cannot normalize path None.") elif len(path) == 0: return path elif path == "/" or path == "\\": return "_" else: normalized = path normalized = re.sub(r"(^[A-Za-z])(:)([\\/])", r"\1-", normalized) # normalize Windows drive letter normalized = re.sub(r"^/", "", normalized) # remove leading '/' normalized = re.sub(r"^\\", "", normalized) # remove leading '\' normalized = re.sub(r"^\.", "_", normalized) # convert leading '.' to '_' so file won't be hidden normalized = re.sub(r"/", "-", normalized) # convert all '/' characters to '-' normalized = re.sub(r"\\", "-", normalized) # convert all '\' characters to '-' normalized = re.sub(r"\s", "_", normalized) # convert all whitespace to '_' normalized = re.sub(r":", "_", normalized) # convert all remaining colons to '_' return normalized ################################# # sanitizeEnvironment() function ################################# def sanitizeEnvironment(): """ Sanitizes the operating system environment. The operating system environment is contained in ``os.environ``. This method sanitizes the contents of that dictionary. Currently, all it does is reset the locale (removing ``$LC_*``) and set the default language (``$LANG``) to ``DEFAULT_LANGUAGE``. This way, we can count on consistent localization regardless of what the end-user has configured. This is important for code that needs to parse program output. The ``os.environ`` dictionary is modifed in-place. If ``$LANG`` is already set to the proper value, it is not re-set, so we can avoid the memory leaks that are documented to occur on BSD-based systems. Returns: Copy of the sanitized environment """ for var in LOCALE_VARS: if var in os.environ: del os.environ[var] if LANG_VAR in os.environ: if os.environ[LANG_VAR] != DEFAULT_LANGUAGE: # no need to reset if it exists (avoid leaks on BSD systems) os.environ[LANG_VAR] = DEFAULT_LANGUAGE return os.environ.copy() ############################# # dereferenceLink() function ############################# def dereferenceLink(path, absolute=True): """ Deference a soft link, optionally normalizing it to an absolute path. Args: path: Path of link to dereference absolute: Whether to normalize the result to an absolute path Returns: Dereferenced path, or original path if original is not a link """ if os.path.islink(path): result = os.readlink(path) if absolute and not os.path.isabs(result): result = os.path.abspath(os.path.join(os.path.dirname(path), result)) return result return path ######################### # checkUnique() function ######################### def checkUnique(prefix, values): """ Checks that all values are unique. The values list is checked for duplicate values. If there are duplicates, an exception is thrown. All duplicate values are listed in the exception. Args: prefix: Prefix to use in the thrown exception values: List of values to check Raises: ValueError: If there are duplicates in the list """ values.sort() duplicates = [] for i in range(1, len(values)): if values[i - 1] == values[i]: duplicates.append(values[i]) if duplicates: raise ValueError("%s %s" % (prefix, duplicates)) ####################################### # parseCommaSeparatedString() function ####################################### def parseCommaSeparatedString(commaString): """ Parses a list of values out of a comma-separated string. The items in the list are split by comma, and then have whitespace stripped. As a special case, if ``commaString`` is ``None``, then ``None`` will be returned. Args: commaString: List of values in comma-separated string format Returns: Values from commaString split into a list, or ``None`` """ if commaString is None: return None else: pass1 = commaString.split(",") pass2 = [] for item in pass1: item = item.strip() if len(item) > 0: pass2.append(item) return pass2 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/writer.py0000644000000000000000000000301714567004737016061 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides interface backwards compatibility. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides interface backwards compatibility. In Cedar Backup 2.10.0, a refactoring effort took place while adding code to support DVD hardware. All of the writer functionality was moved to the writers/ package. This mostly-empty file remains to preserve the Cedar Backup library interface. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## from CedarBackup3.writers.cdwriter import ( # pylint: disable=W0611 MEDIA_CDR_74, MEDIA_CDR_80, MEDIA_CDRW_74, MEDIA_CDRW_80, CdWriter, MediaCapacity, MediaDefinition, ) from CedarBackup3.writers.util import validateDriveSpeed, validateScsiId # pylint: disable=W0611 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/writers/__init__.py0000644000000000000000000000256614567004737020013 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Official Cedar Backup Extensions # Purpose : Provides package initialization # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Cedar Backup writers. This package consolidates all of the modules that implenent "image writer" functionality, including utilities and specific writer implementations. :author: Kenneth J. Pronovici """ ######################################################################## # Package initialization ######################################################################## # Using 'from CedarBackup3.writers import *' will just import the modules listed # in the __all__ variable. import CedarBackup3.writers.cdwriter import CedarBackup3.writers.dvdwriter import CedarBackup3.writers.util __all__ = ["util", "cdwriter", "dvdwriter"] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/writers/cdwriter.py0000644000000000000000000015355314567004737020102 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides functionality related to CD writer devices. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides functionality related to CD writer devices. Module Attributes ================= Attributes: MEDIA_CDRW_74: Constant representing 74-minute CD-RW media MEDIA_CDR_74: Constant representing 74-minute CD-R media MEDIA_CDRW_80: Constant representing 80-minute CD-RW media MEDIA_CDR_80: Constant representing 80-minute CD-R media :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import re import tempfile import time from CedarBackup3.util import ( UNIT_BYTES, UNIT_KBYTES, UNIT_MBYTES, UNIT_SECTORS, convertSize, displayBytes, encodePath, executeCommand, resolveCommand, ) from CedarBackup3.writers.util import IsoImage, validateDevice, validateDriveSpeed, validateScsiId ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.writers.cdwriter") MEDIA_CDRW_74 = 1 MEDIA_CDR_74 = 2 MEDIA_CDRW_80 = 3 MEDIA_CDR_80 = 4 CDRECORD_COMMAND = ["cdrecord"] EJECT_COMMAND = ["eject"] MKISOFS_COMMAND = ["mkisofs"] ######################################################################## # MediaDefinition class definition ######################################################################## class MediaDefinition(object): """ Class encapsulating information about CD media definitions. The following media types are accepted: - ``MEDIA_CDR_74``: 74-minute CD-R media (650 MB capacity) - ``MEDIA_CDRW_74``: 74-minute CD-RW media (650 MB capacity) - ``MEDIA_CDR_80``: 80-minute CD-R media (700 MB capacity) - ``MEDIA_CDRW_80``: 80-minute CD-RW media (700 MB capacity) Note that all of the capacities associated with a media definition are in terms of ISO sectors (``util.ISO_SECTOR_SIZE)``. """ def __init__(self, mediaType): """ Creates a media definition for the indicated media type. Args: mediaType: Type of the media, as discussed above Raises: ValueError: If the media type is unknown or unsupported """ self._mediaType = None self._rewritable = False self._initialLeadIn = 0.0 self._leadIn = 0.0 self._capacity = 0.0 self._setValues(mediaType) def _setValues(self, mediaType): """ Sets values based on media type. Args: mediaType: Type of the media, as discussed above Raises: ValueError: If the media type is unknown or unsupported """ if mediaType not in [MEDIA_CDR_74, MEDIA_CDRW_74, MEDIA_CDR_80, MEDIA_CDRW_80]: raise ValueError("Invalid media type %d." % mediaType) self._mediaType = mediaType self._initialLeadIn = 11400.0 # per cdrecord's documentation self._leadIn = 6900.0 # per cdrecord's documentation if self._mediaType == MEDIA_CDR_74: self._rewritable = False self._capacity = convertSize(650.0, UNIT_MBYTES, UNIT_SECTORS) elif self._mediaType == MEDIA_CDRW_74: self._rewritable = True self._capacity = convertSize(650.0, UNIT_MBYTES, UNIT_SECTORS) elif self._mediaType == MEDIA_CDR_80: self._rewritable = False self._capacity = convertSize(700.0, UNIT_MBYTES, UNIT_SECTORS) elif self._mediaType == MEDIA_CDRW_80: self._rewritable = True self._capacity = convertSize(700.0, UNIT_MBYTES, UNIT_SECTORS) def _getMediaType(self): """ Property target used to get the media type value. """ return self._mediaType def _getRewritable(self): """ Property target used to get the rewritable flag value. """ return self._rewritable def _getInitialLeadIn(self): """ Property target used to get the initial lead-in value. """ return self._initialLeadIn def _getLeadIn(self): """ Property target used to get the lead-in value. """ return self._leadIn def _getCapacity(self): """ Property target used to get the capacity value. """ return self._capacity mediaType = property(_getMediaType, None, None, doc="Configured media type.") rewritable = property(_getRewritable, None, None, doc="Boolean indicating whether the media is rewritable.") initialLeadIn = property(_getInitialLeadIn, None, None, doc="Initial lead-in required for first image written to media.") leadIn = property(_getLeadIn, None, None, doc="Lead-in required on successive images written to media.") capacity = property(_getCapacity, None, None, doc="Total capacity of the media before any required lead-in.") ######################################################################## # MediaCapacity class definition ######################################################################## class MediaCapacity(object): """ Class encapsulating information about CD media capacity. Space used includes the required media lead-in (unless the disk is unused). Space available attempts to provide a picture of how many bytes are available for data storage, including any required lead-in. The boundaries value is either ``None`` (if multisession discs are not supported or if the disc has no boundaries) or in exactly the form provided by ``cdrecord -msinfo``. It can be passed as-is to the ``IsoImage`` class. """ def __init__(self, bytesUsed, bytesAvailable, boundaries): """ Initializes a capacity object. Raises: IndexError: If the boundaries tuple does not have enough elements ValueError: If the boundaries values are not integers ValueError: If the bytes used and available values are not floats """ self._bytesUsed = float(bytesUsed) self._bytesAvailable = float(bytesAvailable) if boundaries is None: self._boundaries = None else: self._boundaries = (int(boundaries[0]), int(boundaries[1])) def __str__(self): """ Informal string representation for class instance. """ return "utilized %s of %s (%.2f%%)" % (displayBytes(self.bytesUsed), displayBytes(self.totalCapacity), self.utilized) def _getBytesUsed(self): """ Property target to get the bytes-used value. """ return self._bytesUsed def _getBytesAvailable(self): """ Property target to get the bytes-available value. """ return self._bytesAvailable def _getBoundaries(self): """ Property target to get the boundaries tuple. """ return self._boundaries def _getTotalCapacity(self): """ Property target to get the total capacity (used + available). """ return self.bytesUsed + self.bytesAvailable def _getUtilized(self): """ Property target to get the percent of capacity which is utilized. """ if self.bytesAvailable <= 0.0: return 100.0 elif self.bytesUsed <= 0.0: return 0.0 return (self.bytesUsed / self.totalCapacity) * 100.0 bytesUsed = property(_getBytesUsed, None, None, doc="Space used on disc, in bytes.") bytesAvailable = property(_getBytesAvailable, None, None, doc="Space available on disc, in bytes.") boundaries = property(_getBoundaries, None, None, doc="Session disc boundaries, in terms of ISO sectors.") totalCapacity = property(_getTotalCapacity, None, None, doc="Total capacity of the disc, in bytes.") utilized = property(_getUtilized, None, None, "Percentage of the total capacity which is utilized.") ######################################################################## # _ImageProperties class definition ######################################################################## class _ImageProperties(object): """ Simple value object to hold image properties for ``DvdWriter``. """ def __init__(self): self.newDisc = False self.tmpdir = None self.mediaLabel = None self.entries = None # dict mapping path to graft point ######################################################################## # CdWriter class definition ######################################################################## class CdWriter(object): ###################### # Class documentation ###################### """ Class representing a device that knows how to write CD media. This is a class representing a device that knows how to write CD media. It provides common operations for the device, such as ejecting the media, writing an ISO image to the media, or checking for the current media capacity. It also provides a place to store device attributes, such as whether the device supports writing multisession discs, etc. This class is implemented in terms of the ``eject`` and ``cdrecord`` programs, both of which should be available on most UN*X platforms. **Image Writer Interface** The following methods make up the "image writer" interface shared with other kinds of writers (such as DVD writers):: __init__ initializeImage() addImageEntry() writeImage() setImageNewDisc() retrieveCapacity() getEstimatedImageSize() Only these methods will be used by other Cedar Backup functionality that expects a compatible image writer. The media attribute is also assumed to be available. **Media Types** This class knows how to write to two different kinds of media, represented by the following constants: - ``MEDIA_CDR_74``: 74-minute CD-R media (650 MB capacity) - ``MEDIA_CDRW_74``: 74-minute CD-RW media (650 MB capacity) - ``MEDIA_CDR_80``: 80-minute CD-R media (700 MB capacity) - ``MEDIA_CDRW_80``: 80-minute CD-RW media (700 MB capacity) Most hardware can read and write both 74-minute and 80-minute CD-R and CD-RW media. Some older drives may only be able to write CD-R media. The difference between the two is that CD-RW media can be rewritten (erased), while CD-R media cannot be. I do not support any other configurations for a couple of reasons. The first is that I've never tested any other kind of media. The second is that anything other than 74 or 80 minute is apparently non-standard. **Device Attributes vs. Media Attributes** A given writer instance has two different kinds of attributes associated with it, which I call device attributes and media attributes. Device attributes are things which can be determined without looking at the media, such as whether the drive supports writing multisession disks or has a tray. Media attributes are attributes which vary depending on the state of the media, such as the remaining capacity on a disc. In general, device attributes are available via instance variables and are constant over the life of an object, while media attributes can be retrieved through method calls. **Talking to Hardware** This class needs to talk to CD writer hardware in two different ways: through cdrecord to actually write to the media, and through the filesystem to do things like open and close the tray. Historically, CdWriter has interacted with cdrecord using the scsiId attribute, and with most other utilities using the device attribute. This changed somewhat in Cedar Backup 2.9.0. When Cedar Backup was first written, the only way to interact with cdrecord was by using a SCSI device id. IDE devices were mapped to pseudo-SCSI devices through the kernel. Later, extended SCSI "methods" arrived, and it became common to see ``ATA:1,0,0`` or ``ATAPI:0,0,0`` as a way to address IDE hardware. By late 2006, ``ATA`` and ``ATAPI`` had apparently been deprecated in favor of just addressing the IDE device directly by name, i.e. ``/dev/cdrw``. Because of this latest development, it no longer makes sense to require a CdWriter to be created with a SCSI id -- there might not be one. So, the passed-in SCSI id is now optional. Also, there is now a hardwareId attribute. This attribute is filled in with either the SCSI id (if provided) or the device (otherwise). The hardware id is the value that will be passed to cdrecord in the ``dev=`` argument. **Testing** It's rather difficult to test this code in an automated fashion, even if you have access to a physical CD writer drive. It's even more difficult to test it if you are running on some build daemon (think of a Debian autobuilder) which can't be expected to have any hardware or any media that you could write to. Because of this, much of the implementation below is in terms of static methods that are supposed to take defined actions based on their arguments. Public methods are then implemented in terms of a series of calls to simplistic static methods. This way, we can test as much as possible of the functionality via testing the static methods, while hoping that if the static methods are called appropriately, things will work properly. It's not perfect, but it's much better than no testing at all. """ ############## # Constructor ############## def __init__( self, device, scsiId=None, driveSpeed=None, mediaType=MEDIA_CDRW_74, noEject=False, refreshMediaDelay=0, ejectDelay=0, unittest=False, ): """ Initializes a CD writer object. The current user must have write access to the device at the time the object is instantiated, or an exception will be thrown. However, no media-related validation is done, and in fact there is no need for any media to be in the drive until one of the other media attribute-related methods is called. The various instance variables such as ``deviceType``, ``deviceVendor``, etc. might be ``None``, if we're unable to parse this specific information from the ``cdrecord`` output. This information is just for reference. The SCSI id is optional, but the device path is required. If the SCSI id is passed in, then the hardware id attribute will be taken from the SCSI id. Otherwise, the hardware id will be taken from the device. If cdrecord improperly detects whether your writer device has a tray and can be safely opened and closed, then pass in ``noEject=False``. This will override the properties and the device will never be ejected. *Note:* The ``unittest`` parameter should never be set to ``True`` outside of Cedar Backup code. It is intended for use in unit testing Cedar Backup internals and has no other sensible purpose. Args: device (Absolute path to a filesystem device, i.e. ``/dev/cdrw``): Filesystem device associated with this writer scsiId (If provided, SCSI id in the form ``[:]scsibus,target,lun``): SCSI id for the device (optional) driveSpeed (Use ``2`` for 2x device, etc. or ``None`` to use device default): Speed at which the drive writes mediaType (One of the valid media type as discussed above): Type of the media that is assumed to be in the drive noEject (Boolean true/false): Overrides properties to indicate that the device does not support eject refreshMediaDelay (Number of seconds, an integer >= 0): Refresh media delay to use, if any ejectDelay (Number of seconds, an integer >= 0): Eject delay to use, if any unittest (Boolean true/false): Turns off certain validations, for use in unit testing Raises: ValueError: If the device is not valid for some reason ValueError: If the SCSI id is not in a valid form ValueError: If the drive speed is not an integer >= 1 IOError: If device properties could not be read for some reason """ self._image = None # optionally filled in by initializeImage() self._device = validateDevice(device, unittest) self._scsiId = validateScsiId(scsiId) self._driveSpeed = validateDriveSpeed(driveSpeed) self._media = MediaDefinition(mediaType) self._noEject = noEject self._refreshMediaDelay = refreshMediaDelay self._ejectDelay = ejectDelay if not unittest: ( self._deviceType, self._deviceVendor, self._deviceId, self._deviceBufferSize, self._deviceSupportsMulti, self._deviceHasTray, self._deviceCanEject, ) = self._retrieveProperties() ############# # Properties ############# def _getDevice(self): """ Property target used to get the device value. """ return self._device def _getScsiId(self): """ Property target used to get the SCSI id value. """ return self._scsiId def _getHardwareId(self): """ Property target used to get the hardware id value. """ if self._scsiId is None: return self._device return self._scsiId def _getDriveSpeed(self): """ Property target used to get the drive speed. """ return self._driveSpeed def _getMedia(self): """ Property target used to get the media description. """ return self._media def _getDeviceType(self): """ Property target used to get the device type. """ return self._deviceType def _getDeviceVendor(self): """ Property target used to get the device vendor. """ return self._deviceVendor def _getDeviceId(self): """ Property target used to get the device id. """ return self._deviceId def _getDeviceBufferSize(self): """ Property target used to get the device buffer size. """ return self._deviceBufferSize def _getDeviceSupportsMulti(self): """ Property target used to get the device-support-multi flag. """ return self._deviceSupportsMulti def _getDeviceHasTray(self): """ Property target used to get the device-has-tray flag. """ return self._deviceHasTray def _getDeviceCanEject(self): """ Property target used to get the device-can-eject flag. """ return self._deviceCanEject def _getRefreshMediaDelay(self): """ Property target used to get the configured refresh media delay, in seconds. """ return self._refreshMediaDelay def _getEjectDelay(self): """ Property target used to get the configured eject delay, in seconds. """ return self._ejectDelay device = property(_getDevice, None, None, doc="Filesystem device name for this writer.") scsiId = property(_getScsiId, None, None, doc="SCSI id for the device, in the form ``[:]scsibus,target,lun``.") hardwareId = property(_getHardwareId, None, None, doc="Hardware id for this writer, either SCSI id or device path.") driveSpeed = property(_getDriveSpeed, None, None, doc="Speed at which the drive writes.") media = property(_getMedia, None, None, doc="Definition of media that is expected to be in the device.") deviceType = property(_getDeviceType, None, None, doc="Type of the device, as returned from ``cdrecord -prcap``.") deviceVendor = property(_getDeviceVendor, None, None, doc="Vendor of the device, as returned from ``cdrecord -prcap``.") deviceId = property(_getDeviceId, None, None, doc="Device identification, as returned from ``cdrecord -prcap``.") deviceBufferSize = property(_getDeviceBufferSize, None, None, doc="Size of the device's write buffer, in bytes.") deviceSupportsMulti = property(_getDeviceSupportsMulti, None, None, doc="Indicates whether device supports multisession discs.") deviceHasTray = property(_getDeviceHasTray, None, None, doc="Indicates whether the device has a media tray.") deviceCanEject = property(_getDeviceCanEject, None, None, doc="Indicates whether the device supports ejecting its media.") refreshMediaDelay = property(_getRefreshMediaDelay, None, None, doc="Refresh media delay, in seconds.") ejectDelay = property(_getEjectDelay, None, None, doc="Eject delay, in seconds.") ################################################# # Methods related to device and media attributes ################################################# def isRewritable(self): """Indicates whether the media is rewritable per configuration.""" return self._media.rewritable def _retrieveProperties(self): """ Retrieves properties for a device from ``cdrecord``. The results are returned as a tuple of the object device attributes as returned from :any:`_parsePropertiesOutput`: C{(deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject)}. Returns: Results tuple as described above Raises: IOError: If there is a problem talking to the device """ args = CdWriter._buildPropertiesArgs(self.hardwareId) command = resolveCommand(CDRECORD_COMMAND) (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) if result != 0: raise IOError("Error (%d) executing cdrecord command to get properties." % result) return CdWriter._parsePropertiesOutput(output) def retrieveCapacity(self, entireDisc=False, useMulti=True): """ Retrieves capacity for the current media in terms of a ``MediaCapacity`` object. If ``entireDisc`` is passed in as ``True`` the capacity will be for the entire disc, as if it were to be rewritten from scratch. If the drive does not support writing multisession discs or if ``useMulti`` is passed in as ``False``, the capacity will also be as if the disc were to be rewritten from scratch, but the indicated boundaries value will be ``None``. The same will happen if the disc cannot be read for some reason. Otherwise, the capacity (including the boundaries) will represent whatever space remains on the disc to be filled by future sessions. Args: entireDisc (Boolean true/false): Indicates whether to return capacity for entire disc useMulti (Boolean true/false): Indicates whether a multisession disc should be assumed, if possible Returns: ``MediaCapacity`` object describing the capacity of the media Raises: IOError: If the media could not be read for some reason """ boundaries = self._getBoundaries(entireDisc, useMulti) return CdWriter._calculateCapacity(self._media, boundaries) def _getBoundaries(self, entireDisc=False, useMulti=True): """ Gets the ISO boundaries for the media. If ``entireDisc`` is passed in as ``True`` the boundaries will be ``None``, as if the disc were to be rewritten from scratch. If the drive does not support writing multisession discs, the returned value will be ``None``. The same will happen if the disc can't be read for some reason. Otherwise, the returned value will be represent the boundaries of the disc's current contents. The results are returned as a tuple of (lower, upper) as needed by the ``IsoImage`` class. Note that these values are in terms of ISO sectors, not bytes. Clients should generally consider the boundaries value opaque, however. Args: entireDisc (Boolean true/false): Indicates whether to return capacity for entire disc useMulti (Boolean true/false): Indicates whether a multisession disc should be assumed, if possible Returns: Boundaries tuple or ``None``, as described above Raises: IOError: If the media could not be read for some reason """ if not self._deviceSupportsMulti: logger.debug("Device does not support multisession discs; returning boundaries None.") return None elif not useMulti: logger.debug("Use multisession flag is False; returning boundaries None.") return None elif entireDisc: logger.debug("Entire disc flag is True; returning boundaries None.") return None else: args = CdWriter._buildBoundariesArgs(self.hardwareId) command = resolveCommand(CDRECORD_COMMAND) (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) if result != 0: logger.debug("Error (%d) executing cdrecord command to get capacity.", result) logger.warning("Unable to read disc (might not be initialized); returning boundaries of None.") return None boundaries = CdWriter._parseBoundariesOutput(output) if boundaries is None: logger.debug("Returning disc boundaries: None") else: logger.debug("Returning disc boundaries: (%d, %d)", boundaries[0], boundaries[1]) return boundaries @staticmethod def _calculateCapacity(media, boundaries): """ Calculates capacity for the media in terms of boundaries. If ``boundaries`` is ``None`` or the lower bound is 0 (zero), then the capacity will be for the entire disc minus the initial lead in. Otherwise, capacity will be as if the caller wanted to add an additional session to the end of the existing data on the disc. Args: media: MediaDescription object describing the media capacity boundaries: Session boundaries as returned from :any:`_getBoundaries` Returns: ``MediaCapacity`` object describing the capacity of the media """ if boundaries is None or boundaries[1] == 0: logger.debug("Capacity calculations are based on a complete disc rewrite.") sectorsAvailable = media.capacity - media.initialLeadIn if sectorsAvailable < 0: sectorsAvailable = 0.0 bytesUsed = 0.0 bytesAvailable = convertSize(sectorsAvailable, UNIT_SECTORS, UNIT_BYTES) else: logger.debug("Capacity calculations are based on a new ISO session.") sectorsAvailable = media.capacity - boundaries[1] - media.leadIn if sectorsAvailable < 0: sectorsAvailable = 0.0 bytesUsed = convertSize(boundaries[1], UNIT_SECTORS, UNIT_BYTES) bytesAvailable = convertSize(sectorsAvailable, UNIT_SECTORS, UNIT_BYTES) logger.debug("Used [%s], available [%s].", displayBytes(bytesUsed), displayBytes(bytesAvailable)) return MediaCapacity(bytesUsed, bytesAvailable, boundaries) ####################################################### # Methods used for working with the internal ISO image ####################################################### def initializeImage(self, newDisc, tmpdir, mediaLabel=None): """ Initializes the writer's associated ISO image. This method initializes the ``image`` instance variable so that the caller can use the ``addImageEntry`` method. Once entries have been added, the ``writeImage`` method can be called with no arguments. Args: newDisc (Boolean true/false): Indicates whether the disc should be re-initialized tmpdir (String representing a directory path on disk): Temporary directory to use if needed mediaLabel (String, no more than 25 characters long): Media label to be applied to the image, if any """ self._image = _ImageProperties() self._image.newDisc = newDisc self._image.tmpdir = encodePath(tmpdir) self._image.mediaLabel = mediaLabel self._image.entries = {} # mapping from path to graft point (if any) def addImageEntry(self, path, graftPoint): """ Adds a filepath entry to the writer's associated ISO image. The contents of the filepath -- but not the path itself -- will be added to the image at the indicated graft point. If you don't want to use a graft point, just pass ``None``. *Note:* Before calling this method, you must call :any:`initializeImage`. Args: path (String representing a path on disk): File or directory to be added to the image graftPoint (String representing a graft point path, as described above): Graft point to be used when adding this entry Raises: ValueError: If initializeImage() was not previously called """ if self._image is None: raise ValueError("Must call initializeImage() before using this method.") if not os.path.exists(path): raise ValueError("Path [%s] does not exist." % path) self._image.entries[path] = graftPoint def setImageNewDisc(self, newDisc): """ Resets (overrides) the newDisc flag on the internal image. Args: newDisc: New disc flag to set Raises: ValueError: If initializeImage() was not previously called """ if self._image is None: raise ValueError("Must call initializeImage() before using this method.") self._image.newDisc = newDisc def getEstimatedImageSize(self): """ Gets the estimated size of the image associated with the writer. Returns: Estimated size of the image, in bytes Raises: IOError: If there is a problem calling ``mkisofs`` ValueError: If initializeImage() was not previously called """ if self._image is None: raise ValueError("Must call initializeImage() before using this method.") image = IsoImage() for path in list(self._image.entries.keys()): image.addEntry(path, self._image.entries[path], override=False, contentsOnly=True) return image.getEstimatedSize() ###################################### # Methods which expose device actions ###################################### def openTray(self): """ Opens the device's tray and leaves it open. This only works if the device has a tray and supports ejecting its media. We have no way to know if the tray is currently open or closed, so we just send the appropriate command and hope for the best. If the device does not have a tray or does not support ejecting its media, then we do nothing. If the writer was constructed with ``noEject=True``, then this is a no-op. Starting with Debian wheezy on my backup hardware, I started seeing consistent problems with the eject command. I couldn't tell whether these problems were due to the device management system or to the new kernel (3.2.0). Initially, I saw simple eject failures, possibly because I was opening and closing the tray too quickly. I worked around that behavior with the new ejectDelay flag. Later, I sometimes ran into issues after writing an image to a disc: eject would give errors like "unable to eject, last error: Inappropriate ioctl for device". Various sources online (like Ubuntu bug #875543) suggested that the drive was being locked somehow, and that the workaround was to run 'eject -i off' to unlock it. Sure enough, that fixed the problem for me, so now it's a normal error-handling strategy. Raises: IOError: If there is an error talking to the device """ if not self._noEject: if self._deviceHasTray and self._deviceCanEject: args = CdWriter._buildOpenTrayArgs(self._device) result = executeCommand(EJECT_COMMAND, args)[0] if result != 0: logger.debug("Eject failed; attempting kludge of unlocking the tray before retrying.") self.unlockTray() result = executeCommand(EJECT_COMMAND, args)[0] if result != 0: raise IOError( "Error (%d) executing eject command to open tray (failed even after unlocking tray)." % result ) logger.debug("Kludge was apparently successful.") if self.ejectDelay is not None: logger.debug("Per configuration, sleeping %d seconds after opening tray.", self.ejectDelay) time.sleep(self.ejectDelay) def unlockTray(self): """ Unlocks the device's tray. Raises: IOError: If there is an error talking to the device """ args = CdWriter._buildUnlockTrayArgs(self._device) command = resolveCommand(EJECT_COMMAND) result = executeCommand(command, args)[0] if result != 0: raise IOError("Error (%d) executing eject command to unlock tray." % result) def closeTray(self): """ Closes the device's tray. This only works if the device has a tray and supports ejecting its media. We have no way to know if the tray is currently open or closed, so we just send the appropriate command and hope for the best. If the device does not have a tray or does not support ejecting its media, then we do nothing. If the writer was constructed with ``noEject=True``, then this is a no-op. Raises: IOError: If there is an error talking to the device """ if not self._noEject: if self._deviceHasTray and self._deviceCanEject: args = CdWriter._buildCloseTrayArgs(self._device) command = resolveCommand(EJECT_COMMAND) result = executeCommand(command, args)[0] if result != 0: raise IOError("Error (%d) executing eject command to close tray." % result) def refreshMedia(self): """ Opens and then immediately closes the device's tray, to refresh the device's idea of the media. Sometimes, a device gets confused about the state of its media. Often, all it takes to solve the problem is to eject the media and then immediately reload it. (There are also configurable eject and refresh media delays which can be applied, for situations where this makes a difference.) This only works if the device has a tray and supports ejecting its media. We have no way to know if the tray is currently open or closed, so we just send the appropriate command and hope for the best. If the device does not have a tray or does not support ejecting its media, then we do nothing. The configured delays still apply, though. Raises: IOError: If there is an error talking to the device """ self.openTray() self.closeTray() self.unlockTray() # on some systems, writing a disc leaves the tray locked, yikes! if self.refreshMediaDelay is not None: logger.debug("Per configuration, sleeping %d seconds to stabilize media state.", self.refreshMediaDelay) time.sleep(self.refreshMediaDelay) logger.debug("Media refresh complete; hopefully media state is stable now.") def writeImage(self, imagePath=None, newDisc=False, writeMulti=True): """ Writes an ISO image to the media in the device. If ``newDisc`` is passed in as ``True``, we assume that the entire disc will be overwritten, and the media will be blanked before writing it if possible (i.e. if the media is rewritable). If ``writeMulti`` is passed in as ``True``, then a multisession disc will be written if possible (i.e. if the drive supports writing multisession discs). if ``imagePath`` is passed in as ``None``, then the existing image configured with ``initializeImage`` will be used. Under these circumstances, the passed-in ``newDisc`` flag will be ignored. By default, we assume that the disc can be written multisession and that we should append to the current contents of the disc. In any case, the ISO image must be generated appropriately (i.e. must take into account any existing session boundaries, etc.) Args: imagePath (String representing a path on disk): Path to an ISO image on disk, or ``None`` to use writer's image newDisc (Boolean true/false): Indicates whether the entire disc will overwritten writeMulti (Boolean true/false): Indicates whether a multisession disc should be written, if possible Raises: ValueError: If the image path is not absolute ValueError: If some path cannot be encoded properly IOError: If the media could not be written to for some reason ValueError: If no image is passed in and initializeImage() was not previously called """ if imagePath is None: if self._image is None: raise ValueError("Must call initializeImage() before using this method with no image path.") try: imagePath = self._createImage() self._writeImage(imagePath, writeMulti, self._image.newDisc) finally: if imagePath is not None and os.path.exists(imagePath): try: os.unlink(imagePath) except: pass else: imagePath = encodePath(imagePath) if not os.path.isabs(imagePath): raise ValueError("Image path must be absolute.") self._writeImage(imagePath, writeMulti, newDisc) def _createImage(self): """ Creates an ISO image based on configuration in self._image. Returns: Path to the newly-created ISO image on disk Raises: IOError: If there is an error writing the image to disk ValueError: If there are no filesystem entries in the image ValueError: If a path cannot be encoded properly """ path = None capacity = self.retrieveCapacity(entireDisc=self._image.newDisc) image = IsoImage(self.device, capacity.boundaries) image.volumeId = self._image.mediaLabel # may be None, which is also valid for key in list(self._image.entries.keys()): image.addEntry(key, self._image.entries[key], override=False, contentsOnly=True) size = image.getEstimatedSize() logger.info("Image size will be %s.", displayBytes(size)) available = capacity.bytesAvailable logger.debug("Media capacity: %s", displayBytes(available)) if size > available: logger.error("Image [%s] does not fit in available capacity [%s].", displayBytes(size), displayBytes(available)) raise IOError("Media does not contain enough capacity to store image.") try: (handle, path) = tempfile.mkstemp(dir=self._image.tmpdir) try: os.close(handle) except: pass image.writeImage(path) logger.debug("Completed creating image [%s].", path) return path except Exception as e: if path is not None and os.path.exists(path): try: os.unlink(path) except: pass raise e def _writeImage(self, imagePath, writeMulti, newDisc): """ Write an ISO image to disc using cdrecord. The disc is blanked first if ``newDisc`` is ``True``. Args: imagePath: Path to an ISO image on disk writeMulti: Indicates whether a multisession disc should be written, if possible newDisc: Indicates whether the entire disc will overwritten """ if newDisc: self._blankMedia() args = CdWriter._buildWriteArgs(self.hardwareId, imagePath, self._driveSpeed, writeMulti and self._deviceSupportsMulti) command = resolveCommand(CDRECORD_COMMAND) result = executeCommand(command, args)[0] if result != 0: raise IOError("Error (%d) executing command to write disc." % result) self.refreshMedia() def _blankMedia(self): """ Blanks the media in the device, if the media is rewritable. Raises: IOError: If the media could not be written to for some reason """ if self.isRewritable(): args = CdWriter._buildBlankArgs(self.hardwareId) command = resolveCommand(CDRECORD_COMMAND) result = executeCommand(command, args)[0] if result != 0: raise IOError("Error (%d) executing command to blank disc." % result) self.refreshMedia() ####################################### # Methods used to parse command output ####################################### @staticmethod def _parsePropertiesOutput(output): """ Parses the output from a ``cdrecord`` properties command. The ``output`` parameter should be a list of strings as returned from ``executeCommand`` for a ``cdrecord`` command with arguments as from ``_buildPropertiesArgs``. The list of strings will be parsed to yield information about the properties of the device. The output is expected to be a huge long list of strings. Unfortunately, the strings aren't in a completely regular format. However, the format of individual lines seems to be regular enough that we can look for specific values. Two kinds of parsing take place: one kind of parsing picks out out specific values like the device id, device vendor, etc. The other kind of parsing just sets a boolean flag ``True`` if a matching line is found. All of the parsing is done with regular expressions. Right now, pretty much nothing in the output is required and we should parse an empty document successfully (albeit resulting in a device that can't eject, doesn't have a tray and doesnt't support multisession discs). I had briefly considered erroring out if certain lines weren't found or couldn't be parsed, but that seems like a bad idea given that most of the information is just for reference. The results are returned as a tuple of the object device attributes: C{(deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject)}. Args: output: Output from a ``cdrecord -prcap`` command Returns: Results tuple as described above Raises: IOError: If there is problem parsing the output """ deviceType = None deviceVendor = None deviceId = None deviceBufferSize = None deviceSupportsMulti = False deviceHasTray = False deviceCanEject = False typePattern = re.compile(r"(^Device type\s*:\s*)(.*)(\s*)(.*$)") vendorPattern = re.compile(r"(^Vendor_info\s*:\s*'\s*)(.*?)(\s*')(.*$)") idPattern = re.compile(r"(^Identifikation\s*:\s*'\s*)(.*?)(\s*')(.*$)") bufferPattern = re.compile(r"(^\s*Buffer size in KB:\s*)(.*?)(\s*$)") multiPattern = re.compile(r"^\s*Does read multi-session.*$") trayPattern = re.compile(r"^\s*Loading mechanism type: tray.*$") ejectPattern = re.compile(r"^\s*Does support ejection.*$") for line in output: if typePattern.search(line): deviceType = typePattern.search(line).group(2) logger.info("Device type is [%s].", deviceType) elif vendorPattern.search(line): deviceVendor = vendorPattern.search(line).group(2) logger.info("Device vendor is [%s].", deviceVendor) elif idPattern.search(line): deviceId = idPattern.search(line).group(2) logger.info("Device id is [%s].", deviceId) elif bufferPattern.search(line): try: sectors = int(bufferPattern.search(line).group(2)) deviceBufferSize = convertSize(sectors, UNIT_KBYTES, UNIT_BYTES) logger.info("Device buffer size is [%d] bytes.", deviceBufferSize) except TypeError: pass elif multiPattern.search(line): deviceSupportsMulti = True logger.info("Device does support multisession discs.") elif trayPattern.search(line): deviceHasTray = True logger.info("Device has a tray.") elif ejectPattern.search(line): deviceCanEject = True logger.info("Device can eject its media.") return (deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject) @staticmethod def _parseBoundariesOutput(output): """ Parses the output from a ``cdrecord`` capacity command. The ``output`` parameter should be a list of strings as returned from ``executeCommand`` for a ``cdrecord`` command with arguments as from ``_buildBoundaryArgs``. The list of strings will be parsed to yield information about the capacity of the media in the device. Basically, we expect the list of strings to include just one line, a pair of values. There isn't supposed to be whitespace, but we allow it anyway in the regular expression. Any lines below the one line we parse are completely ignored. It would be a good idea to ignore ``stderr`` when executing the ``cdrecord`` command that generates output for this method, because sometimes ``cdrecord`` spits out kernel warnings about the actual output. The results are returned as a tuple of (lower, upper) as needed by the ``IsoImage`` class. Note that these values are in terms of ISO sectors, not bytes. Clients should generally consider the boundaries value opaque, however. *Note:* If the boundaries output can't be parsed, we return ``None``. Args: output: Output from a ``cdrecord -msinfo`` command Returns: Boundaries tuple as described above Raises: IOError: If there is problem parsing the output """ if len(output) < 1: logger.warning("Unable to read disc (might not be initialized); returning full capacity.") return None boundaryPattern = re.compile(r"(^\s*)([0-9]*)(\s*,\s*)([0-9]*)(\s*$)") parsed = boundaryPattern.search(output[0]) if not parsed: raise IOError("Unable to parse output of boundaries command.") try: boundaries = (int(parsed.group(2)), int(parsed.group(4))) except TypeError: raise IOError("Unable to parse output of boundaries command.") return boundaries ################################# # Methods used to build commands ################################# @staticmethod def _buildOpenTrayArgs(device): """ Builds a list of arguments to be passed to a ``eject`` command. The arguments will cause the ``eject`` command to open the tray and eject the media. No validation is done by this method as to whether this action actually makes sense. Args: device: Filesystem device name for this writer, i.e. ``/dev/cdrw`` Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = [] args.append(device) return args @staticmethod def _buildUnlockTrayArgs(device): """ Builds a list of arguments to be passed to a ``eject`` command. The arguments will cause the ``eject`` command to unlock the tray. Args: device: Filesystem device name for this writer, i.e. ``/dev/cdrw`` Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = [] args.append("-i") args.append("off") args.append(device) return args @staticmethod def _buildCloseTrayArgs(device): """ Builds a list of arguments to be passed to a ``eject`` command. The arguments will cause the ``eject`` command to close the tray and reload the media. No validation is done by this method as to whether this action actually makes sense. Args: device: Filesystem device name for this writer, i.e. ``/dev/cdrw`` Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = [] args.append("-t") args.append(device) return args @staticmethod def _buildPropertiesArgs(hardwareId): """ Builds a list of arguments to be passed to a ``cdrecord`` command. The arguments will cause the ``cdrecord`` command to ask the device for a list of its capacities via the ``-prcap`` switch. Args: hardwareId: Hardware id for the device (either SCSI id or device path) Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = [] args.append("-prcap") args.append("dev=%s" % hardwareId) return args @staticmethod def _buildBoundariesArgs(hardwareId): """ Builds a list of arguments to be passed to a ``cdrecord`` command. The arguments will cause the ``cdrecord`` command to ask the device for the current multisession boundaries of the media using the ``-msinfo`` switch. Args: hardwareId: Hardware id for the device (either SCSI id or device path) Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = [] args.append("-msinfo") args.append("dev=%s" % hardwareId) return args @staticmethod def _buildBlankArgs(hardwareId, driveSpeed=None): """ Builds a list of arguments to be passed to a ``cdrecord`` command. The arguments will cause the ``cdrecord`` command to blank the media in the device identified by ``hardwareId``. No validation is done by this method as to whether the action makes sense (i.e. to whether the media even can be blanked). Args: hardwareId: Hardware id for the device (either SCSI id or device path) driveSpeed: Speed at which the drive writes Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = [] args.append("-v") args.append("blank=fast") if driveSpeed is not None: args.append("speed=%d" % driveSpeed) args.append("dev=%s" % hardwareId) return args @staticmethod def _buildWriteArgs(hardwareId, imagePath, driveSpeed=None, writeMulti=True): """ Builds a list of arguments to be passed to a ``cdrecord`` command. The arguments will cause the ``cdrecord`` command to write the indicated ISO image (``imagePath``) to the media in the device identified by ``hardwareId``. The ``writeMulti`` argument controls whether to write a multisession disc. No validation is done by this method as to whether the action makes sense (i.e. to whether the device even can write multisession discs, for instance). Args: hardwareId: Hardware id for the device (either SCSI id or device path) imagePath: Path to an ISO image on disk driveSpeed: Speed at which the drive writes writeMulti: Indicates whether to write a multisession disc Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = [] args.append("-v") if driveSpeed is not None: args.append("speed=%d" % driveSpeed) args.append("dev=%s" % hardwareId) if writeMulti: args.append("-multi") args.append("-data") args.append(imagePath) return args ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/writers/dvdwriter.py0000644000000000000000000012051714567004737020263 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides functionality related to DVD writer devices. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides functionality related to DVD writer devices. Module Attributes ================= Attributes: MEDIA_DVDPLUSR: Constant representing DVD+R media MEDIA_DVDPLUSRW: Constant representing DVD+RW media :author: Kenneth J. Pronovici :author: Dmitry Rutsky """ ######################################################################## # Imported modules ######################################################################## import logging import os import re import tempfile import time from CedarBackup3.util import ( UNIT_BYTES, UNIT_GBYTES, UNIT_SECTORS, convertSize, displayBytes, encodePath, executeCommand, resolveCommand, ) from CedarBackup3.writers.util import IsoImage, validateDevice, validateDriveSpeed ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.writers.dvdwriter") MEDIA_DVDPLUSR = 1 MEDIA_DVDPLUSRW = 2 GROWISOFS_COMMAND = ["growisofs"] EJECT_COMMAND = ["eject"] ######################################################################## # MediaDefinition class definition ######################################################################## class MediaDefinition(object): """ Class encapsulating information about DVD media definitions. The following media types are accepted: - ``MEDIA_DVDPLUSR``: DVD+R media (4.4 GB capacity) - ``MEDIA_DVDPLUSRW``: DVD+RW media (4.4 GB capacity) Note that the capacity attribute returns capacity in terms of ISO sectors (``util.ISO_SECTOR_SIZE)``. This is for compatibility with the CD writer functionality. The capacities are 4.4 GB because Cedar Backup deals in "true" gigabytes of 1024*1024*1024 bytes per gigabyte. """ def __init__(self, mediaType): """ Creates a media definition for the indicated media type. Args: mediaType: Type of the media, as discussed above Raises: ValueError: If the media type is unknown or unsupported """ self._mediaType = None self._rewritable = False self._capacity = 0.0 self._setValues(mediaType) def _setValues(self, mediaType): """ Sets values based on media type. Args: mediaType: Type of the media, as discussed above Raises: ValueError: If the media type is unknown or unsupported """ if mediaType not in [MEDIA_DVDPLUSR, MEDIA_DVDPLUSRW]: raise ValueError("Invalid media type %d." % mediaType) self._mediaType = mediaType if self._mediaType == MEDIA_DVDPLUSR: self._rewritable = False self._capacity = convertSize(4.4, UNIT_GBYTES, UNIT_SECTORS) # 4.4 "true" GB = 4.7 "marketing" GB elif self._mediaType == MEDIA_DVDPLUSRW: self._rewritable = True self._capacity = convertSize(4.4, UNIT_GBYTES, UNIT_SECTORS) # 4.4 "true" GB = 4.7 "marketing" GB def _getMediaType(self): """ Property target used to get the media type value. """ return self._mediaType def _getRewritable(self): """ Property target used to get the rewritable flag value. """ return self._rewritable def _getCapacity(self): """ Property target used to get the capacity value. """ return self._capacity mediaType = property(_getMediaType, None, None, doc="Configured media type.") rewritable = property(_getRewritable, None, None, doc="Boolean indicating whether the media is rewritable.") capacity = property(_getCapacity, None, None, doc="Total capacity of media in 2048-byte sectors.") ######################################################################## # MediaCapacity class definition ######################################################################## class MediaCapacity(object): """ Class encapsulating information about DVD media capacity. Space used and space available do not include any information about media lead-in or other overhead. """ def __init__(self, bytesUsed, bytesAvailable): """ Initializes a capacity object. Raises: ValueError: If the bytes used and available values are not floats """ self._bytesUsed = float(bytesUsed) self._bytesAvailable = float(bytesAvailable) def __str__(self): """ Informal string representation for class instance. """ return "utilized %s of %s (%.2f%%)" % (displayBytes(self.bytesUsed), displayBytes(self.totalCapacity), self.utilized) def _getBytesUsed(self): """ Property target used to get the bytes-used value. """ return self._bytesUsed def _getBytesAvailable(self): """ Property target available to get the bytes-available value. """ return self._bytesAvailable def _getTotalCapacity(self): """ Property target to get the total capacity (used + available). """ return self.bytesUsed + self.bytesAvailable def _getUtilized(self): """ Property target to get the percent of capacity which is utilized. """ if self.bytesAvailable <= 0.0: return 100.0 elif self.bytesUsed <= 0.0: return 0.0 return (self.bytesUsed / self.totalCapacity) * 100.0 bytesUsed = property(_getBytesUsed, None, None, doc="Space used on disc, in bytes.") bytesAvailable = property(_getBytesAvailable, None, None, doc="Space available on disc, in bytes.") totalCapacity = property(_getTotalCapacity, None, None, doc="Total capacity of the disc, in bytes.") utilized = property(_getUtilized, None, None, "Percentage of the total capacity which is utilized.") ######################################################################## # _ImageProperties class definition ######################################################################## class _ImageProperties(object): """ Simple value object to hold image properties for ``DvdWriter``. """ def __init__(self): self.newDisc = False self.tmpdir = None self.mediaLabel = None self.entries = None # dict mapping path to graft point ######################################################################## # DvdWriter class definition ######################################################################## class DvdWriter(object): ###################### # Class documentation ###################### """ Class representing a device that knows how to write some kinds of DVD media. **Summary** This is a class representing a device that knows how to write some kinds of DVD media. It provides common operations for the device, such as ejecting the media and writing data to the media. This class is implemented in terms of the ``eject`` and ``growisofs`` utilities, all of which should be available on most UN*X platforms. **Image Writer Interface** The following methods make up the "image writer" interface shared with other kinds of writers:: __init__ initializeImage() addImageEntry() writeImage() setImageNewDisc() retrieveCapacity() getEstimatedImageSize() Only these methods will be used by other Cedar Backup functionality that expects a compatible image writer. The media attribute is also assumed to be available. Unlike the ``CdWriter``, the ``DvdWriter`` can only operate in terms of filesystem devices, not SCSI devices. So, although the constructor interface accepts a SCSI device parameter for the sake of compatibility, it's not used. **Media Types** This class knows how to write to DVD+R and DVD+RW media, represented by the following constants: - ``MEDIA_DVDPLUSR``: DVD+R media (4.4 GB capacity) - ``MEDIA_DVDPLUSRW``: DVD+RW media (4.4 GB capacity) The difference is that DVD+RW media can be rewritten, while DVD+R media cannot be (although at present, ``DvdWriter`` does not really differentiate between rewritable and non-rewritable media). The capacities are 4.4 GB because Cedar Backup deals in "true" gigabytes of 1024*1024*1024 bytes per gigabyte. The underlying ``growisofs`` utility does support other kinds of media (including DVD-R, DVD-RW and BlueRay) which work somewhat differently than standard DVD+R and DVD+RW media. I don't support these other kinds of media because I haven't had any opportunity to work with them. The same goes for dual-layer media of any type. **Device Attributes vs. Media Attributes** As with the cdwriter functionality, a given dvdwriter instance has two different kinds of attributes associated with it. I call these device attributes and media attributes. Device attributes are things which can be determined without looking at the media. Media attributes are attributes which vary depending on the state of the media. In general, device attributes are available via instance variables and are constant over the life of an object, while media attributes can be retrieved through method calls. Compared to cdwriters, dvdwriters have very few attributes. This is due to differences between the way ``growisofs`` works relative to ``cdrecord``. **Media Capacity** One major difference between the ``cdrecord``/``mkisofs`` utilities used by the cdwriter class and the ``growisofs`` utility used here is that the process of estimating remaining capacity and image size is more straightforward with ``cdrecord``/``mkisofs`` than with ``growisofs``. In this class, remaining capacity is calculated by asking doing a dry run of ``growisofs`` and grabbing some information from the output of that command. Image size is estimated by asking the ``IsoImage`` class for an estimate and then adding on a "fudge factor" determined through experimentation. **Testing** It's rather difficult to test this code in an automated fashion, even if you have access to a physical DVD writer drive. It's even more difficult to test it if you are running on some build daemon (think of a Debian autobuilder) which can't be expected to have any hardware or any media that you could write to. Because of this, some of the implementation below is in terms of static methods that are supposed to take defined actions based on their arguments. Public methods are then implemented in terms of a series of calls to simplistic static methods. This way, we can test as much as possible of the "difficult" functionality via testing the static methods, while hoping that if the static methods are called appropriately, things will work properly. It's not perfect, but it's much better than no testing at all. """ ############## # Constructor ############## def __init__( self, device, scsiId=None, driveSpeed=None, mediaType=MEDIA_DVDPLUSRW, noEject=False, refreshMediaDelay=0, ejectDelay=0, unittest=False, ): """ Initializes a DVD writer object. Since ``growisofs`` can only address devices using the device path (i.e. ``/dev/dvd``), the hardware id will always be set based on the device. If passed in, it will be saved for reference purposes only. We have no way to query the device to ask whether it has a tray or can be safely opened and closed. So, the ``noEject`` flag is used to set these values. If ``noEject=False``, then we assume a tray exists and open/close is safe. If ``noEject=True``, then we assume that there is no tray and open/close is not safe. The SCSI id, if provided, is in the form ``[:]scsibus,target,lun``. *Note:* The ``unittest`` parameter should never be set to ``True`` outside of Cedar Backup code. It is intended for use in unit testing Cedar Backup internals and has no other sensible purpose. Args: device: Absolute path to the writer filesystem device, like ``/dev/dvd`` scsiId: SCSI id for the device (optional, for reference only) driveSpeed: Speed at which the drive writes, like 2 for a 2x device, or None for device default mediaType: Type of the media that is assumed to be in the drive noEject (Boolean true/false): Tells Cedar Backup that the device cannot safely be ejected refreshMediaDelay (Integer >= 0): Refresh media delay to use, if any ejectDelay (Integer >= 0): Eject delay to use, if any unittest (Boolean true/false): Turns off certain validations, for use in unit testing Raises: ValueError: If the device is not valid for some reason ValueError: If the SCSI id is not in a valid form ValueError: If the drive speed is not an integer >= 1 """ if scsiId is not None: logger.warning("SCSI id [%s] will be ignored by DvdWriter.", scsiId) self._image = None # optionally filled in by initializeImage() self._device = validateDevice(device, unittest) self._scsiId = scsiId # not validated, because it's just for reference self._driveSpeed = validateDriveSpeed(driveSpeed) self._media = MediaDefinition(mediaType) self._refreshMediaDelay = refreshMediaDelay self._ejectDelay = ejectDelay if noEject: self._deviceHasTray = False self._deviceCanEject = False else: self._deviceHasTray = True # just assume self._deviceCanEject = True # just assume ############# # Properties ############# def _getDevice(self): """ Property target used to get the device value. """ return self._device def _getScsiId(self): """ Property target used to get the SCSI id value. """ return self._scsiId def _getHardwareId(self): """ Property target used to get the hardware id value. """ return self._device def _getDriveSpeed(self): """ Property target used to get the drive speed. """ return self._driveSpeed def _getMedia(self): """ Property target used to get the media description. """ return self._media def _getDeviceHasTray(self): """ Property target used to get the device-has-tray flag. """ return self._deviceHasTray def _getDeviceCanEject(self): """ Property target used to get the device-can-eject flag. """ return self._deviceCanEject def _getRefreshMediaDelay(self): """ Property target used to get the configured refresh media delay, in seconds. """ return self._refreshMediaDelay def _getEjectDelay(self): """ Property target used to get the configured eject delay, in seconds. """ return self._ejectDelay device = property(_getDevice, None, None, doc="Filesystem device name for this writer.") scsiId = property(_getScsiId, None, None, doc="SCSI id for the device (saved for reference only).") hardwareId = property(_getHardwareId, None, None, doc="Hardware id for this writer (always the device path).") driveSpeed = property(_getDriveSpeed, None, None, doc="Speed at which the drive writes.") media = property(_getMedia, None, None, doc="Definition of media that is expected to be in the device.") deviceHasTray = property(_getDeviceHasTray, None, None, doc="Indicates whether the device has a media tray.") deviceCanEject = property(_getDeviceCanEject, None, None, doc="Indicates whether the device supports ejecting its media.") refreshMediaDelay = property(_getRefreshMediaDelay, None, None, doc="Refresh media delay, in seconds.") ejectDelay = property(_getEjectDelay, None, None, doc="Eject delay, in seconds.") ################################################# # Methods related to device and media attributes ################################################# def isRewritable(self): """Indicates whether the media is rewritable per configuration.""" return self._media.rewritable def retrieveCapacity(self, entireDisc=False): """ Retrieves capacity for the current media in terms of a ``MediaCapacity`` object. If ``entireDisc`` is passed in as ``True``, the capacity will be for the entire disc, as if it were to be rewritten from scratch. The same will happen if the disc can't be read for some reason. Otherwise, the capacity will be calculated by subtracting the sectors currently used on the disc, as reported by ``growisofs`` itself. Args: entireDisc (Boolean true/false): Indicates whether to return capacity for entire disc Returns: ``MediaCapacity`` object describing the capacity of the media Raises: ValueError: If there is a problem parsing the ``growisofs`` output IOError: If the media could not be read for some reason """ sectorsUsed = 0.0 if not entireDisc: sectorsUsed = self._retrieveSectorsUsed() sectorsAvailable = self._media.capacity - sectorsUsed # both are in sectors bytesUsed = convertSize(sectorsUsed, UNIT_SECTORS, UNIT_BYTES) bytesAvailable = convertSize(sectorsAvailable, UNIT_SECTORS, UNIT_BYTES) return MediaCapacity(bytesUsed, bytesAvailable) ####################################################### # Methods used for working with the internal ISO image ####################################################### def initializeImage(self, newDisc, tmpdir, mediaLabel=None): """ Initializes the writer's associated ISO image. This method initializes the ``image`` instance variable so that the caller can use the ``addImageEntry`` method. Once entries have been added, the ``writeImage`` method can be called with no arguments. Args: newDisc (Boolean true/false): Indicates whether the disc should be re-initialized tmpdir (String representing a directory path on disk): Temporary directory to use if needed mediaLabel (String, no more than 25 characters long): Media label to be applied to the image, if any """ self._image = _ImageProperties() self._image.newDisc = newDisc self._image.tmpdir = encodePath(tmpdir) self._image.mediaLabel = mediaLabel self._image.entries = {} # mapping from path to graft point (if any) def addImageEntry(self, path, graftPoint): """ Adds a filepath entry to the writer's associated ISO image. The contents of the filepath -- but not the path itself -- will be added to the image at the indicated graft point. If you don't want to use a graft point, just pass ``None``. *Note:* Before calling this method, you must call :any:`initializeImage`. Args: path (String representing a path on disk): File or directory to be added to the image graftPoint (String representing a graft point path, as described above): Graft point to be used when adding this entry Raises: ValueError: If initializeImage() was not previously called ValueError: If the path is not a valid file or directory """ if self._image is None: raise ValueError("Must call initializeImage() before using this method.") if not os.path.exists(path): raise ValueError("Path [%s] does not exist." % path) self._image.entries[path] = graftPoint def setImageNewDisc(self, newDisc): """ Resets (overrides) the newDisc flag on the internal image. Args: newDisc: New disc flag to set Raises: ValueError: If initializeImage() was not previously called """ if self._image is None: raise ValueError("Must call initializeImage() before using this method.") self._image.newDisc = newDisc def getEstimatedImageSize(self): """ Gets the estimated size of the image associated with the writer. This is an estimate and is conservative. The actual image could be as much as 450 blocks (sectors) smaller under some circmstances. Returns: Estimated size of the image, in bytes Raises: IOError: If there is a problem calling ``mkisofs`` ValueError: If initializeImage() was not previously called """ if self._image is None: raise ValueError("Must call initializeImage() before using this method.") return DvdWriter._getEstimatedImageSize(self._image.entries) ###################################### # Methods which expose device actions ###################################### def openTray(self): """ Opens the device's tray and leaves it open. This only works if the device has a tray and supports ejecting its media. We have no way to know if the tray is currently open or closed, so we just send the appropriate command and hope for the best. If the device does not have a tray or does not support ejecting its media, then we do nothing. Starting with Debian wheezy on my backup hardware, I started seeing consistent problems with the eject command. I couldn't tell whether these problems were due to the device management system or to the new kernel (3.2.0). Initially, I saw simple eject failures, possibly because I was opening and closing the tray too quickly. I worked around that behavior with the new ejectDelay flag. Later, I sometimes ran into issues after writing an image to a disc: eject would give errors like "unable to eject, last error: Inappropriate ioctl for device". Various sources online (like Ubuntu bug #875543) suggested that the drive was being locked somehow, and that the workaround was to run 'eject -i off' to unlock it. Sure enough, that fixed the problem for me, so now it's a normal error-handling strategy. Raises: IOError: If there is an error talking to the device """ if self._deviceHasTray and self._deviceCanEject: command = resolveCommand(EJECT_COMMAND) args = [self.device] result = executeCommand(command, args)[0] if result != 0: logger.debug("Eject failed; attempting kludge of unlocking the tray before retrying.") self.unlockTray() result = executeCommand(command, args)[0] if result != 0: raise IOError("Error (%d) executing eject command to open tray (failed even after unlocking tray)." % result) logger.debug("Kludge was apparently successful.") if self.ejectDelay is not None: logger.debug("Per configuration, sleeping %d seconds after opening tray.", self.ejectDelay) time.sleep(self.ejectDelay) def unlockTray(self): """ Unlocks the device's tray via 'eject -i off'. Raises: IOError: If there is an error talking to the device """ command = resolveCommand(EJECT_COMMAND) args = ["-i", "off", self.device] result = executeCommand(command, args)[0] if result != 0: raise IOError("Error (%d) executing eject command to unlock tray." % result) def closeTray(self): """ Closes the device's tray. This only works if the device has a tray and supports ejecting its media. We have no way to know if the tray is currently open or closed, so we just send the appropriate command and hope for the best. If the device does not have a tray or does not support ejecting its media, then we do nothing. Raises: IOError: If there is an error talking to the device """ if self._deviceHasTray and self._deviceCanEject: command = resolveCommand(EJECT_COMMAND) args = ["-t", self.device] result = executeCommand(command, args)[0] if result != 0: raise IOError("Error (%d) executing eject command to close tray." % result) def refreshMedia(self): """ Opens and then immediately closes the device's tray, to refresh the device's idea of the media. Sometimes, a device gets confused about the state of its media. Often, all it takes to solve the problem is to eject the media and then immediately reload it. (There are also configurable eject and refresh media delays which can be applied, for situations where this makes a difference.) This only works if the device has a tray and supports ejecting its media. We have no way to know if the tray is currently open or closed, so we just send the appropriate command and hope for the best. If the device does not have a tray or does not support ejecting its media, then we do nothing. The configured delays still apply, though. Raises: IOError: If there is an error talking to the device """ self.openTray() self.closeTray() self.unlockTray() # on some systems, writing a disc leaves the tray locked, yikes! if self.refreshMediaDelay is not None: logger.debug("Per configuration, sleeping %d seconds to stabilize media state.", self.refreshMediaDelay) time.sleep(self.refreshMediaDelay) logger.debug("Media refresh complete; hopefully media state is stable now.") def writeImage(self, imagePath=None, newDisc=False, writeMulti=True): """ Writes an ISO image to the media in the device. If ``newDisc`` is passed in as ``True``, we assume that the entire disc will be re-created from scratch. Note that unlike ``CdWriter``, ``DvdWriter`` does not blank rewritable media before reusing it; however, ``growisofs`` is called such that the media will be re-initialized as needed. If ``imagePath`` is passed in as ``None``, then the existing image configured with ``initializeImage()`` will be used. Under these circumstances, the passed-in ``newDisc`` flag will be ignored and the value passed in to ``initializeImage()`` will apply instead. The ``writeMulti`` argument is ignored. It exists for compatibility with the Cedar Backup image writer interface. *Note:* The image size indicated in the log ("Image size will be...") is an estimate. The estimate is conservative and is probably larger than the actual space that ``dvdwriter`` will use. Args: imagePath (String representing a path on disk): Path to an ISO image on disk, or ``None`` to use writer's image newDisc (Boolean true/false): Indicates whether the disc should be re-initialized writeMulti (Boolean true/false): Unused Raises: ValueError: If the image path is not absolute ValueError: If some path cannot be encoded properly IOError: If the media could not be written to for some reason ValueError: If no image is passed in and initializeImage() was not previously called """ if not writeMulti: logger.warning("writeMulti value of [%s] ignored.", writeMulti) if imagePath is None: if self._image is None: raise ValueError("Must call initializeImage() before using this method with no image path.") size = self.getEstimatedImageSize() logger.info("Image size will be %s (estimated).", displayBytes(size)) available = self.retrieveCapacity(entireDisc=self._image.newDisc).bytesAvailable if size > available: logger.error("Image [%s] does not fit in available capacity [%s].", displayBytes(size), displayBytes(available)) raise IOError("Media does not contain enough capacity to store image.") self._writeImage(self._image.newDisc, None, self._image.entries, self._image.mediaLabel) else: if not os.path.isabs(imagePath): raise ValueError("Image path must be absolute.") imagePath = encodePath(imagePath) self._writeImage(newDisc, imagePath, None) ################################################################## # Utility methods for dealing with growisofs and dvd+rw-mediainfo ################################################################## def _writeImage(self, newDisc, imagePath, entries, mediaLabel=None): """ Writes an image to disc using either an entries list or an ISO image on disk. Callers are assumed to have done validation on paths, etc. before calling this method. Args: newDisc: Indicates whether the disc should be re-initialized imagePath: Path to an ISO image on disk, or c{None} to use ``entries`` entries: Mapping from path to graft point, or ``None`` to use ``imagePath`` Raises: IOError: If the media could not be written to for some reason """ command = resolveCommand(GROWISOFS_COMMAND) args = DvdWriter._buildWriteArgs(newDisc, self.hardwareId, self._driveSpeed, imagePath, entries, mediaLabel, dryRun=False) (result, output) = executeCommand(command, args, returnOutput=True) if result != 0: DvdWriter._searchForOverburn(output) # throws own exception if overburn condition is found raise IOError("Error (%d) executing command to write disc." % result) self.refreshMedia() @staticmethod def _getEstimatedImageSize(entries): """ Gets the estimated size of a set of image entries. This is implemented in terms of the ``IsoImage`` class. The returned value is calculated by adding a "fudge factor" to the value from ``IsoImage``. This fudge factor was determined by experimentation and is conservative -- the actual image could be as much as 450 blocks smaller under some circumstances. Args: entries: Dictionary mapping path to graft point Returns: Total estimated size of image, in bytes Raises: ValueError: If there are no entries in the dictionary ValueError: If any path in the dictionary does not exist IOError: If there is a problem calling ``mkisofs`` """ fudgeFactor = convertSize(2500.0, UNIT_SECTORS, UNIT_BYTES) # determined through experimentation if len(list(entries.keys())) == 0: raise ValueError("Must add at least one entry with addImageEntry().") image = IsoImage() for path in list(entries.keys()): image.addEntry(path, entries[path], override=False, contentsOnly=True) estimatedSize = image.getEstimatedSize() + fudgeFactor return estimatedSize def _retrieveSectorsUsed(self): """ Retrieves the number of sectors used on the current media. This is a little ugly. We need to call growisofs in "dry-run" mode and parse some information from its output. However, to do that, we need to create a dummy file that we can pass to the command -- and we have to make sure to remove it later. Once growisofs has been run, then we call ``_parseSectorsUsed`` to parse the output and calculate the number of sectors used on the media. Returns: Number of sectors used on the media """ tempdir = tempfile.mkdtemp() try: entries = {tempdir: None} args = DvdWriter._buildWriteArgs(False, self.hardwareId, self.driveSpeed, None, entries, None, dryRun=True) command = resolveCommand(GROWISOFS_COMMAND) (result, output) = executeCommand(command, args, returnOutput=True) if result != 0: logger.debug("Error (%d) calling growisofs to read sectors used.", result) logger.warning("Unable to read disc (might not be initialized); returning zero sectors used.") return 0.0 sectorsUsed = DvdWriter._parseSectorsUsed(output) logger.debug("Determined sectors used as %s", sectorsUsed) return sectorsUsed finally: if os.path.exists(tempdir): try: os.rmdir(tempdir) except: pass @staticmethod def _parseSectorsUsed(output): """ Parse sectors used information out of ``growisofs`` output. The first line of a growisofs run looks something like this:: Executing 'mkisofs -C 973744,1401056 -M /dev/fd/3 -r -graft-points music4/=music | builtin_dd of=/dev/cdrom obs=32k seek=87566' Dmitry has determined that the seek value in this line gives us information about how much data has previously been written to the media. That value multiplied by 16 yields the number of sectors used. If the seek line cannot be found in the output, then sectors used of zero is assumed. Returns: Sectors used on the media, as a floating point number Raises: ValueError: If the output cannot be parsed properly """ if output is not None: pattern = re.compile(r"(^)(.*)(seek=)(.*)('$)") for line in output: match = pattern.search(line) if match is not None: try: return float(match.group(4).strip()) * 16.0 except ValueError: raise ValueError("Unable to parse sectors used out of growisofs output.") logger.warning("Unable to read disc (might not be initialized); returning zero sectors used.") return 0.0 @staticmethod def _searchForOverburn(output): """ Search for an "overburn" error message in ``growisofs`` output. The ``growisofs`` command returns a non-zero exit code and puts a message into the output -- even on a dry run -- if there is not enough space on the media. This is called an "overburn" condition. The error message looks like this:: :-( /dev/cdrom: 894048 blocks are free, 2033746 to be written! This method looks for the overburn error message anywhere in the output. If a matching error message is found, an ``IOError`` exception is raised containing relevant information about the problem. Otherwise, the method call returns normally. Args: output: List of output lines to search, as from ``executeCommand`` Raises: IOError: If an overburn condition is found """ if output is None: return pattern = re.compile(r"(^)(:-[(])(\s*.*:\s*)(.* )(blocks are free, )(.* )(to be written!)") for line in output: match = pattern.search(line) if match is not None: try: available = convertSize(float(match.group(4).strip()), UNIT_SECTORS, UNIT_BYTES) size = convertSize(float(match.group(6).strip()), UNIT_SECTORS, UNIT_BYTES) logger.error("Image [%s] does not fit in available capacity [%s].", displayBytes(size), displayBytes(available)) except ValueError: logger.error("Image does not fit in available capacity (no useful capacity info available).") raise IOError("Media does not contain enough capacity to store image.") @staticmethod def _buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel=None, dryRun=False): """ Builds a list of arguments to be passed to a ``growisofs`` command. The arguments will either cause ``growisofs`` to write the indicated image file to disc, or will pass ``growisofs`` a list of directories or files that should be written to disc. If a new image is created, it will always be created with Rock Ridge extensions (-r). A volume name will be applied (-V) if ``mediaLabel`` is not ``None``. Args: newDisc: Indicates whether the disc should be re-initialized hardwareId: Hardware id for the device driveSpeed: Speed at which the drive writes imagePath: Path to an ISO image on disk, or c{None} to use ``entries`` entries: Mapping from path to graft point, or ``None`` to use ``imagePath`` mediaLabel: Media label to set on the image, if any dryRun: Says whether to make this a dry run (for checking capacity) *Note:* If we write an existing image to disc, then the mediaLabel is ignored. The media label is an attribute of the image, and should be set on the image when it is created. *Note:* We always pass the undocumented option ``-use-the-force-like=tty`` to growisofs. Without this option, growisofs will refuse to execute certain actions when running from cron. A good example is -Z, which happily overwrites an existing DVD from the command-line, but fails when run from cron. It took a while to figure that out, since it worked every time I tested it by hand. :( Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` Raises: ValueError: If caller does not pass one or the other of imagePath or entries """ args = [] if (imagePath is None and entries is None) or (imagePath is not None and entries is not None): raise ValueError("Must use either imagePath or entries.") args.append("-use-the-force-luke=tty") # tell growisofs to let us run from cron if dryRun: args.append("-dry-run") if driveSpeed is not None: args.append("-speed=%d" % driveSpeed) if newDisc: args.append("-Z") else: args.append("-M") if imagePath is not None: args.append("%s=%s" % (hardwareId, imagePath)) else: args.append(hardwareId) if mediaLabel is not None: args.append("-V") args.append(mediaLabel) args.append("-r") # Rock Ridge extensions with sane ownership and permissions args.append("-graft-points") keys = list(entries.keys()) keys.sort() # just so we get consistent results for key in keys: # Same syntax as when calling mkisofs in IsoImage if entries[key] is None: args.append(key) else: args.append("%s/=%s" % (entries[key].strip("/"), key)) return args ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/writers/util.py0000644000000000000000000007000314567004737017220 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides utilities related to image writers. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides utilities related to image writers. :author: Kenneth J. Pronovici """ ######################################################################## # Imported modules ######################################################################## import logging import os import re from CedarBackup3.util import UNIT_BYTES, UNIT_SECTORS, convertSize, encodePath, executeCommand, pathJoin, resolveCommand ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.writers.util") MKISOFS_COMMAND = ["mkisofs"] VOLNAME_COMMAND = ["volname"] ######################################################################## # Functions used to portably validate certain kinds of values ######################################################################## ############################ # validateDevice() function ############################ def validateDevice(device, unittest=False): """ Validates a configured device. The device must be an absolute path, must exist, and must be writable. The unittest flag turns off validation of the device on disk. Args: device: Filesystem device path unittest: Indicates whether we're unit testing Returns: Device as a string, for instance ``"/dev/cdrw"`` Raises: ValueError: If the device value is invalid ValueError: If some path cannot be encoded properly """ if device is None: raise ValueError("Device must be filled in.") device = encodePath(device) if not os.path.isabs(device): raise ValueError("Backup device must be an absolute path.") if not unittest and not os.path.exists(device): raise ValueError("Backup device must exist on disk.") if not unittest and not os.access(device, os.W_OK): raise ValueError("Backup device is not writable by the current user.") return device ############################ # validateScsiId() function ############################ def validateScsiId(scsiId): """ Validates a SCSI id string. SCSI id must be a string in the form ``[:]scsibus,target,lun``. For Mac OS X (Darwin), we also accept the form ``IO.*Services[/N]``. *Note:* For consistency, if ``None`` is passed in, ``None`` will be returned. Args: scsiId: SCSI id for the device Returns: SCSI id as a string, for instance ``"ATA:1,0,0"`` Raises: ValueError: If the SCSI id string is invalid """ if scsiId is not None: pattern = re.compile(r"^\s*(.*:)?\s*[0-9][0-9]*\s*,\s*[0-9][0-9]*\s*,\s*[0-9][0-9]*\s*$") if not pattern.search(scsiId): pattern = re.compile(r"^\s*IO.*Services(/[0-9][0-9]*)?\s*$") if not pattern.search(scsiId): raise ValueError("SCSI id is not in a valid form.") return scsiId ################################ # validateDriveSpeed() function ################################ def validateDriveSpeed(driveSpeed): """ Validates a drive speed value. Drive speed must be an integer which is >= 1. *Note:* For consistency, if ``None`` is passed in, ``None`` will be returned. Args: driveSpeed: Speed at which the drive writes Returns: Drive speed as an integer Raises: ValueError: If the drive speed value is invalid """ if driveSpeed is None: return None try: intSpeed = int(driveSpeed) except TypeError: raise ValueError("Drive speed must be an integer >= 1.") if intSpeed < 1: raise ValueError("Drive speed must an integer >= 1.") return intSpeed ######################################################################## # General writer-related utility functions ######################################################################## ############################ # readMediaLabel() function ############################ def readMediaLabel(devicePath): """ Reads the media label (volume name) from the indicated device. The volume name is read using the ``volname`` command. Args: devicePath: Device path to read from Returns: Media label as a string, or None if there is no name or it could not be read """ args = [devicePath] command = resolveCommand(VOLNAME_COMMAND) (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) if result != 0: return None if output is None or len(output) < 1: return None return output[0].rstrip() ######################################################################## # IsoImage class definition ######################################################################## class IsoImage(object): ###################### # Class documentation ###################### """ Represents an ISO filesystem image. **Summary** This object represents an ISO 9660 filesystem image. It is implemented in terms of the ``mkisofs`` program, which has been ported to many operating systems and platforms. A "sensible subset" of the ``mkisofs`` functionality is made available through the public interface, allowing callers to set a variety of basic options such as publisher id, application id, etc. as well as specify exactly which files and directories they want included in their image. By default, the image is created using the Rock Ridge protocol (using the ``-r`` option to ``mkisofs``) because Rock Ridge discs are generally more useful on UN*X filesystems than standard ISO 9660 images. However, callers can fall back to the default ``mkisofs`` functionality by setting the ``useRockRidge`` instance variable to ``False``. Note, however, that this option is not well-tested. **Where Files and Directories are Placed in the Image** Although this class is implemented in terms of the ``mkisofs`` program, its standard "image contents" semantics are slightly different than the original ``mkisofs`` semantics. The difference is that files and directories are added to the image with some additional information about their source directory kept intact. As an example, suppose you add the file ``/etc/profile`` to your image and you do not configure a graft point. The file ``/profile`` will be created in the image. The behavior for directories is similar. For instance, suppose that you add ``/etc/X11`` to the image and do not configure a graft point. In this case, the directory ``/X11`` will be created in the image, even if the original ``/etc/X11`` directory is empty. I{This behavior differs from the standard ``mkisofs`` behavior!} If a graft point is configured, it will be used to modify the point at which a file or directory is added into an image. Using the examples from above, let's assume you set a graft point of ``base`` when adding ``/etc/profile`` and ``/etc/X11`` to your image. In this case, the file ``/base/profile`` and the directory ``/base/X11`` would be added to the image. I feel that this behavior is more consistent than the original ``mkisofs`` behavior. However, to be fair, it is not quite as flexible, and some users might not like it. For this reason, the ``contentsOnly`` parameter to the :any:`addEntry` method can be used to revert to the original behavior if desired. """ ############## # Constructor ############## def __init__(self, device=None, boundaries=None, graftPoint=None): """ Initializes an empty ISO image object. Only the most commonly-used configuration items can be set using this constructor. If you have a need to change the others, do so immediately after creating your object. The device and boundaries values are both required in order to write multisession discs. If either is missing or ``None``, a multisession disc will not be written. The boundaries tuple is in terms of ISO sectors, as built by an image writer class and returned in a ``writer.MediaCapacity`` object. The boundaries parameter is a tuple of ``(last_sess_start, next_sess_start)``, as returned from ``cdrecord -msinfo``. Args: device (Either be a filesystem path or a SCSI address): Name of the device that the image will be written to boundaries: Session boundaries as required by ``mkisofs``, or ``None`` graftPoint (String representing a graft point path (see :any:`addEntry`)): Default graft point for this page """ self._device = None self._boundaries = None self._graftPoint = None self._useRockRidge = True self._applicationId = None self._biblioFile = None self._publisherId = None self._preparerId = None self._volumeId = None self.entries = {} self.device = device self.boundaries = boundaries self.graftPoint = graftPoint self.useRockRidge = True self.applicationId = None self.biblioFile = None self.publisherId = None self.preparerId = None self.volumeId = None logger.debug("Created new ISO image object.") ############# # Properties ############# def _setDevice(self, value): """ Property target used to set the device value. If not ``None``, the value can be either an absolute path or a SCSI id. Raises: ValueError: If the value is not valid """ try: if value is None: self._device = None else: if os.path.isabs(value): self._device = value else: self._device = validateScsiId(value) except ValueError: raise ValueError("Device must either be an absolute path or a valid SCSI id.") def _getDevice(self): """ Property target used to get the device value. """ return self._device def _setBoundaries(self, value): """ Property target used to set the boundaries tuple. If not ``None``, the value must be a tuple of two integers. Raises: ValueError: If the tuple values are not integers IndexError: If the tuple does not contain enough elements """ if value is None: self._boundaries = None else: self._boundaries = (int(value[0]), int(value[1])) def _getBoundaries(self): """ Property target used to get the boundaries value. """ return self._boundaries def _setGraftPoint(self, value): """ Property target used to set the graft point. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The graft point must be a non-empty string.") self._graftPoint = value def _getGraftPoint(self): """ Property target used to get the graft point. """ return self._graftPoint def _setUseRockRidge(self, value): """ Property target used to set the use RockRidge flag. No validations, but we normalize the value to ``True`` or ``False``. """ if value: self._useRockRidge = True else: self._useRockRidge = False def _getUseRockRidge(self): """ Property target used to get the use RockRidge flag. """ return self._useRockRidge def _setApplicationId(self, value): """ Property target used to set the application id. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The application id must be a non-empty string.") self._applicationId = value def _getApplicationId(self): """ Property target used to get the application id. """ return self._applicationId def _setBiblioFile(self, value): """ Property target used to set the biblio file. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The biblio file must be a non-empty string.") self._biblioFile = value def _getBiblioFile(self): """ Property target used to get the biblio file. """ return self._biblioFile def _setPublisherId(self, value): """ Property target used to set the publisher id. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The publisher id must be a non-empty string.") self._publisherId = value def _getPublisherId(self): """ Property target used to get the publisher id. """ return self._publisherId def _setPreparerId(self, value): """ Property target used to set the preparer id. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The preparer id must be a non-empty string.") self._preparerId = value def _getPreparerId(self): """ Property target used to get the preparer id. """ return self._preparerId def _setVolumeId(self, value): """ Property target used to set the volume id. The value must be a non-empty string if it is not ``None``. Raises: ValueError: If the value is an empty string """ if value is not None: if len(value) < 1: raise ValueError("The volume id must be a non-empty string.") self._volumeId = value def _getVolumeId(self): """ Property target used to get the volume id. """ return self._volumeId device = property(_getDevice, _setDevice, None, "Device that image will be written to (device path or SCSI id).") boundaries = property(_getBoundaries, _setBoundaries, None, "Session boundaries as required by ``mkisofs``.") graftPoint = property(_getGraftPoint, _setGraftPoint, None, "Default image-wide graft point (see :any:`addEntry` for details).") useRockRidge = property(_getUseRockRidge, _setUseRockRidge, None, "Indicates whether to use RockRidge (default is ``True``).") applicationId = property( _getApplicationId, _setApplicationId, None, "Optionally specifies the ISO header application id value." ) biblioFile = property(_getBiblioFile, _setBiblioFile, None, "Optionally specifies the ISO bibliographic file name.") publisherId = property(_getPublisherId, _setPublisherId, None, "Optionally specifies the ISO header publisher id value.") preparerId = property(_getPreparerId, _setPreparerId, None, "Optionally specifies the ISO header preparer id value.") volumeId = property(_getVolumeId, _setVolumeId, None, "Optionally specifies the ISO header volume id value.") ######################### # General public methods ######################### def addEntry(self, path, graftPoint=None, override=False, contentsOnly=False): """ Adds an individual file or directory into the ISO image. The path must exist and must be a file or a directory. By default, the entry will be placed into the image at the root directory, but this behavior can be overridden using the ``graftPoint`` parameter or instance variable. You can use the ``contentsOnly`` behavior to revert to the "original" ``mkisofs`` behavior for adding directories, which is to add only the items within the directory, and not the directory itself. *Note:* Things get *odd* if you try to add a directory to an image that will be written to a multisession disc, and the same directory already exists in an earlier session on that disc. Not all of the data gets written. You really wouldn't want to do this anyway, I guess. *Note:* An exception will be thrown if the path has already been added to the image, unless the ``override`` parameter is set to ``True``. *Note:* The method ``graftPoints`` parameter overrides the object-wide instance variable. If neither the method parameter or object-wide value is set, the path will be written at the image root. The graft point behavior is determined by the value which is in effect I{at the time this method is called}, so you *must* set the object-wide value before calling this method for the first time, or your image may not be consistent. *Note:* You *cannot* use the local ``graftPoint`` parameter to "turn off" an object-wide instance variable by setting it to ``None``. Python's default argument functionality buys us a lot, but it can't make this method psychic. :) Args: path (String representing a path on disk): File or directory to be added to the image graftPoint (String representing a graft point path, as described above): Graft point to be used when adding this entry override (Boolean true/false): Override an existing entry with the same path contentsOnly (Boolean true/false): Add directory contents only (standard ``mkisofs`` behavior) Raises: ValueError: If path is not a file or directory, or does not exist ValueError: If the path has already been added, and override is not set ValueError: If a path cannot be encoded properly """ path = encodePath(path) if not override: if path in list(self.entries.keys()): raise ValueError("Path has already been added to the image.") if os.path.islink(path): raise ValueError("Path must not be a link.") if os.path.isdir(path): if graftPoint is not None: if contentsOnly: self.entries[path] = graftPoint else: self.entries[path] = pathJoin(graftPoint, os.path.basename(path)) elif self.graftPoint is not None: if contentsOnly: self.entries[path] = self.graftPoint else: self.entries[path] = pathJoin(self.graftPoint, os.path.basename(path)) else: if contentsOnly: self.entries[path] = None else: self.entries[path] = os.path.basename(path) elif os.path.isfile(path): if graftPoint is not None: self.entries[path] = graftPoint elif self.graftPoint is not None: self.entries[path] = self.graftPoint else: self.entries[path] = None else: raise ValueError("Path must be a file or a directory.") def getEstimatedSize(self): """ Returns the estimated size (in bytes) of the ISO image. This is implemented via the ``-print-size`` option to ``mkisofs``, so it might take a bit of time to execute. However, the result is as accurate as we can get, since it takes into account all of the ISO overhead, the true cost of directories in the structure, etc, etc. Returns: Estimated size of the image, in bytes Raises: IOError: If there is a problem calling ``mkisofs`` ValueError: If there are no filesystem entries in the image """ if len(list(self.entries.keys())) == 0: raise ValueError("Image does not contain any entries.") return self._getEstimatedSize(self.entries) def _getEstimatedSize(self, entries): """ Returns the estimated size (in bytes) for the passed-in entries dictionary. Returns: Estimated size of the image, in bytes Raises: IOError: If there is a problem calling ``mkisofs`` """ args = self._buildSizeArgs(entries) command = resolveCommand(MKISOFS_COMMAND) (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) if result != 0: raise IOError("Error (%d) executing mkisofs command to estimate size." % result) if len(output) != 1: raise IOError("Unable to parse mkisofs output.") try: sectors = float(output[0]) size = convertSize(sectors, UNIT_SECTORS, UNIT_BYTES) return size except: raise IOError("Unable to parse mkisofs output.") def writeImage(self, imagePath): """ Writes this image to disk using the image path. Args: imagePath (String representing a path on disk): Path to write image out as Raises: IOError: If there is an error writing the image to disk ValueError: If there are no filesystem entries in the image ValueError: If a path cannot be encoded properly """ imagePath = encodePath(imagePath) if len(list(self.entries.keys())) == 0: raise ValueError("Image does not contain any entries.") args = self._buildWriteArgs(self.entries, imagePath) command = resolveCommand(MKISOFS_COMMAND) (result, output) = executeCommand(command, args, returnOutput=False) if result != 0: raise IOError("Error (%d) executing mkisofs command to build image." % result) ######################################### # Methods used to build mkisofs commands ######################################### @staticmethod def _buildDirEntries(entries): """ Uses an entries dictionary to build a list of directory locations for use by ``mkisofs``. We build a list of entries that can be passed to ``mkisofs``. Each entry is either raw (if no graft point was configured) or in graft-point form as described above (if a graft point was configured). The dictionary keys are the path names, and the values are the graft points, if any. Args: entries: Dictionary of image entries (i.e. self.entries) Returns: List of directory locations for use by ``mkisofs`` """ dirEntries = [] for key in list(entries.keys()): if entries[key] is None: dirEntries.append(key) else: dirEntries.append("%s/=%s" % (entries[key].strip("/"), key)) return dirEntries def _buildGeneralArgs(self): """ Builds a list of general arguments to be passed to a ``mkisofs`` command. The various instance variables (``applicationId``, etc.) are filled into the list of arguments if they are set. By default, we will build a RockRidge disc. If you decide to change this, think hard about whether you know what you're doing. This option is not well-tested. Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = [] if self.applicationId is not None: args.append("-A") args.append(self.applicationId) if self.biblioFile is not None: args.append("-biblio") args.append(self.biblioFile) if self.publisherId is not None: args.append("-publisher") args.append(self.publisherId) if self.preparerId is not None: args.append("-p") args.append(self.preparerId) if self.volumeId is not None: args.append("-V") args.append(self.volumeId) return args def _buildSizeArgs(self, entries): """ Builds a list of arguments to be passed to a ``mkisofs`` command. The various instance variables (``applicationId``, etc.) are filled into the list of arguments if they are set. The command will be built to just return size output (a simple count of sectors via the ``-print-size`` option), rather than an image file on disk. By default, we will build a RockRidge disc. If you decide to change this, think hard about whether you know what you're doing. This option is not well-tested. Args: entries: Dictionary of image entries (i.e. self.entries) Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = self._buildGeneralArgs() args.append("-print-size") args.append("-graft-points") if self.useRockRidge: args.append("-r") if self.device is not None and self.boundaries is not None: args.append("-C") args.append("%d,%d" % (self.boundaries[0], self.boundaries[1])) args.append("-M") args.append(self.device) args.extend(self._buildDirEntries(entries)) return args def _buildWriteArgs(self, entries, imagePath): """ Builds a list of arguments to be passed to a ``mkisofs`` command. The various instance variables (``applicationId``, etc.) are filled into the list of arguments if they are set. The command will be built to write an image to disk. By default, we will build a RockRidge disc. If you decide to change this, think hard about whether you know what you're doing. This option is not well-tested. Args: entries: Dictionary of image entries (i.e. self.entries) imagePath (String representing a path on disk): Path to write image out as Returns: List suitable for passing to :any:`util.executeCommand` as ``args`` """ args = self._buildGeneralArgs() args.append("-graft-points") if self.useRockRidge: args.append("-r") args.append("-o") args.append(imagePath) if self.device is not None and self.boundaries is not None: args.append("-C") args.append("%d,%d" % (self.boundaries[0], self.boundaries[1])) args.append("-M") args.append(self.device) args.extend(self._buildDirEntries(entries)) return args ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/src/CedarBackup3/xmlutil.py0000644000000000000000000006470314567004737016254 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2006,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # Portions Copyright (c) 2000 Fourthought Inc, USA. # All Rights Reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides general XML-related functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Provides general XML-related functionality. What I'm trying to do here is abstract much of the functionality that directly accesses the DOM tree. This is not so much to "protect" the other code from the DOM, but to standardize the way it's used. It will also help extension authors write code that easily looks more like the rest of Cedar Backup. Module Attributes ================= Attributes: TRUE_BOOLEAN_VALUES: List of boolean values in XML representing ``True`` FALSE_BOOLEAN_VALUES: List of boolean values in XML representing ``False`` VALID_BOOLEAN_VALUES: List of valid boolean values in XML :author: Kenneth J. Pronovici """ # pylint: disable=C0111,C0103,W0511,W0104,W0106 ######################################################################## # Imported modules ######################################################################## import logging import re import sys from io import StringIO from xml.dom.minidom import Node, getDOMImplementation, parseString from xml.parsers.expat import ExpatError ######################################################################## # Module-wide constants and variables ######################################################################## logger = logging.getLogger("CedarBackup3.log.xml") TRUE_BOOLEAN_VALUES = ["Y", "y"] FALSE_BOOLEAN_VALUES = ["N", "n"] VALID_BOOLEAN_VALUES = TRUE_BOOLEAN_VALUES + FALSE_BOOLEAN_VALUES ######################################################################## # Functions for creating and parsing DOM trees ######################################################################## def createInputDom(xmlData, name="cb_config"): """ Creates a DOM tree based on reading an XML string. Returns: Tuple (xmlDom, parentNode) for the parsed document Raises: ValueError: If the document can't be parsed """ try: xmlDom = parseString(xmlData) parentNode = readFirstChild(xmlDom, name) return (xmlDom, parentNode) except (IOError, ExpatError) as e: raise ValueError("Unable to parse XML document: %s" % e) def createOutputDom(name="cb_config"): """ Creates a DOM tree used for writing an XML document. Args: name: Base name of the document (root node name) Returns: Tuple (xmlDom, parentNode) for the new document """ impl = getDOMImplementation() xmlDom = impl.createDocument(None, name, None) return (xmlDom, xmlDom.documentElement) ######################################################################## # Functions for reading values out of XML documents ######################################################################## def isElement(node): """ Returns True or False depending on whether the XML node is an element node. """ return node.nodeType == Node.ELEMENT_NODE def readChildren(parent, name): """ Returns a list of nodes with a given name immediately beneath the parent. By "immediately beneath" the parent, we mean from among nodes that are direct children of the passed-in parent node. Underneath, we use the Python ``getElementsByTagName`` method, which is pretty cool, but which (surprisingly?) returns a list of all children with a given name below the parent, at any level. We just prune that list to include only children whose ``parentNode`` matches the passed-in parent. Args: parent: Parent node to search beneath name: Name of nodes to search for Returns: List of child nodes with correct parent, or an empty list if no matching nodes are found. """ lst = [] if parent is not None: result = parent.getElementsByTagName(name) for entry in result: if entry.parentNode is parent: lst.append(entry) return lst def readFirstChild(parent, name): """ Returns the first child with a given name immediately beneath the parent. By "immediately beneath" the parent, we mean from among nodes that are direct children of the passed-in parent node. Args: parent: Parent node to search beneath name: Name of node to search for Returns: First properly-named child of parent, or ``None`` if no matching nodes are found """ result = readChildren(parent, name) if not result: return None return result[0] def readStringList(parent, name): """ Returns a list of the string contents associated with nodes with a given name immediately beneath the parent. By "immediately beneath" the parent, we mean from among nodes that are direct children of the passed-in parent node. First, we find all of the nodes using :any:`readChildren`, and then we retrieve the "string contents" of each of those nodes. The returned list has one entry per matching node. We assume that string contents of a given node belong to the first ``TEXT_NODE`` child of that node. Nodes which have no ``TEXT_NODE`` children are not represented in the returned list. Args: parent: Parent node to search beneath name: Name of node to search for Returns: List of strings as described above, or ``None`` if no matching nodes are found """ lst = [] result = readChildren(parent, name) for entry in result: if entry.hasChildNodes(): for child in entry.childNodes: if child.nodeType == Node.TEXT_NODE: lst.append(child.nodeValue) break if not lst: lst = None return lst def readString(parent, name): """ Returns string contents of the first child with a given name immediately beneath the parent. By "immediately beneath" the parent, we mean from among nodes that are direct children of the passed-in parent node. We assume that string contents of a given node belong to the first ``TEXT_NODE`` child of that node. Args: parent: Parent node to search beneath name: Name of node to search for Returns: String contents of node or ``None`` if no matching nodes are found """ result = readStringList(parent, name) if result is None: return None return result[0] def readInteger(parent, name): """ Returns integer contents of the first child with a given name immediately beneath the parent. By "immediately beneath" the parent, we mean from among nodes that are direct children of the passed-in parent node. Args: parent: Parent node to search beneath name: Name of node to search for Returns: Integer contents of node or ``None`` if no matching nodes are found Raises: ValueError: If the string at the location can't be converted to an integer """ result = readString(parent, name) if result is None: return None else: return int(result) def readLong(parent, name): """ Returns long integer contents of the first child with a given name immediately beneath the parent. By "immediately beneath" the parent, we mean from among nodes that are direct children of the passed-in parent node. Args: parent: Parent node to search beneath name: Name of node to search for Returns: Long integer contents of node or ``None`` if no matching nodes are found Raises: ValueError: If the string at the location can't be converted to an integer """ result = readString(parent, name) if result is None: return None else: return int(result) def readFloat(parent, name): """ Returns float contents of the first child with a given name immediately beneath the parent. By "immediately beneath" the parent, we mean from among nodes that are direct children of the passed-in parent node. Args: parent: Parent node to search beneath name: Name of node to search for Returns: Float contents of node or ``None`` if no matching nodes are found Raises: ValueError: If the string at the location can't be converted to a float value. """ result = readString(parent, name) if result is None: return None else: return float(result) def readBoolean(parent, name): """ Returns boolean contents of the first child with a given name immediately beneath the parent. By "immediately beneath" the parent, we mean from among nodes that are direct children of the passed-in parent node. The string value of the node must be one of the values in :any:`VALID_BOOLEAN_VALUES`. Args: parent: Parent node to search beneath name: Name of node to search for Returns: Boolean contents of node or ``None`` if no matching nodes are found Raises: ValueError: If the string at the location can't be converted to a boolean """ result = readString(parent, name) if result is None: return None else: if result in TRUE_BOOLEAN_VALUES: return True elif result in FALSE_BOOLEAN_VALUES: return False else: raise ValueError("Boolean values must be one of %s." % VALID_BOOLEAN_VALUES) ######################################################################## # Functions for writing values into XML documents ######################################################################## def addContainerNode(xmlDom, parentNode, nodeName): """ Adds a container node as the next child of a parent node. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent node to create child for nodeName: Name of the new container node Returns: Reference to the newly-created node """ containerNode = xmlDom.createElement(nodeName) parentNode.appendChild(containerNode) return containerNode def addStringNode(xmlDom, parentNode, nodeName, nodeValue): """ Adds a text node as the next child of a parent, to contain a string. If the ``nodeValue`` is None, then the node will be created, but will be empty (i.e. will contain no text node child). Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent node to create child for nodeName: Name of the new container node nodeValue: The value to put into the node Returns: Reference to the newly-created node """ containerNode = addContainerNode(xmlDom, parentNode, nodeName) if nodeValue is not None: textNode = xmlDom.createTextNode(nodeValue) containerNode.appendChild(textNode) return containerNode def addIntegerNode(xmlDom, parentNode, nodeName, nodeValue): """ Adds a text node as the next child of a parent, to contain an integer. If the ``nodeValue`` is None, then the node will be created, but will be empty (i.e. will contain no text node child). The integer will be converted to a string using "%d". The result will be added to the document via :any:`addStringNode`. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent node to create child for nodeName: Name of the new container node nodeValue: The value to put into the node Returns: Reference to the newly-created node """ if nodeValue is None: return addStringNode(xmlDom, parentNode, nodeName, None) else: return addStringNode(xmlDom, parentNode, nodeName, "%d" % nodeValue) # %d works for both int and long def addLongNode(xmlDom, parentNode, nodeName, nodeValue): """ Adds a text node as the next child of a parent, to contain a long integer. If the ``nodeValue`` is None, then the node will be created, but will be empty (i.e. will contain no text node child). The integer will be converted to a string using "%d". The result will be added to the document via :any:`addStringNode`. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent node to create child for nodeName: Name of the new container node nodeValue: The value to put into the node Returns: Reference to the newly-created node """ if nodeValue is None: return addStringNode(xmlDom, parentNode, nodeName, None) else: return addStringNode(xmlDom, parentNode, nodeName, "%d" % nodeValue) # %d works for both int and long def addBooleanNode(xmlDom, parentNode, nodeName, nodeValue): """ Adds a text node as the next child of a parent, to contain a boolean. If the ``nodeValue`` is None, then the node will be created, but will be empty (i.e. will contain no text node child). Boolean ``True``, or anything else interpreted as ``True`` by Python, will be converted to a string "Y". Anything else will be converted to a string "N". The result is added to the document via :any:`addStringNode`. Args: xmlDom: DOM tree as from ``impl.createDocument()`` parentNode: Parent node to create child for nodeName: Name of the new container node nodeValue: The value to put into the node Returns: Reference to the newly-created node """ if nodeValue is None: return addStringNode(xmlDom, parentNode, nodeName, None) else: if nodeValue: return addStringNode(xmlDom, parentNode, nodeName, "Y") else: return addStringNode(xmlDom, parentNode, nodeName, "N") ######################################################################## # Functions for serializing DOM trees ######################################################################## def serializeDom(xmlDom, indent=3): """ Serializes a DOM tree and returns the result in a string. Args: xmlDom: XML DOM tree to serialize indent: Number of spaces to indent, as an integer Returns: String form of DOM tree, pretty-printed """ xmlBuffer = StringIO() serializer = Serializer(xmlBuffer, "UTF-8", indent=indent) serializer.serialize(xmlDom) xmlData = xmlBuffer.getvalue() xmlBuffer.close() return xmlData class Serializer(object): """ XML serializer class. This is a customized serializer that I hacked together based on what I found in the PyXML distribution. Basically, around release 2.7.0, the only reason I still had around a dependency on PyXML was for the PrettyPrint functionality, and that seemed pointless. So, I stripped the PrettyPrint code out of PyXML and hacked bits of it off until it did just what I needed and no more. This code started out being called PrintVisitor, but I decided it makes more sense just calling it a serializer. I've made nearly all of the methods private, and I've added a new high-level serialize() method rather than having clients call ``visit()``. Anyway, as a consequence of my hacking with it, this can't quite be called a complete XML serializer any more. I ripped out support for HTML and XHTML, and there is also no longer any support for namespaces (which I took out because this dragged along a lot of extra code, and Cedar Backup doesn't use namespaces). However, everything else should pretty much work as expected. @copyright: This code, prior to customization, was part of the PyXML codebase, and before that was part of the 4DOM suite developed by Fourthought, Inc. It its original form, it was Copyright (c) 2000 Fourthought Inc, USA; All Rights Reserved. """ def __init__(self, stream=sys.stdout, encoding="UTF-8", indent=3): """ Initialize a serializer. Args: stream: Stream to write output to encoding: Output encoding indent: Number of spaces to indent, as an integer """ self.stream = stream self.encoding = encoding self._indent = indent * " " self._depth = 0 self._inText = 0 def serialize(self, xmlDom): """ Serialize the passed-in XML document. Args: xmlDom: XML DOM tree to serialize Raises: ValueError: If there's an unknown node type in the document """ self._visit(xmlDom) self.stream.write("\n") def _write(self, text): obj = _encodeText(text, self.encoding) self.stream.write(obj) return def _tryIndent(self): if not self._inText and self._indent: self._write("\n" + self._indent * self._depth) return def _visit(self, node): """ Raises: ValueError: If there's an unknown node type in the document """ if node.nodeType == Node.ELEMENT_NODE: return self._visitElement(node) elif node.nodeType == Node.ATTRIBUTE_NODE: return self._visitAttr(node) elif node.nodeType == Node.TEXT_NODE: return self._visitText(node) elif node.nodeType == Node.CDATA_SECTION_NODE: return self._visitCDATASection(node) elif node.nodeType == Node.ENTITY_REFERENCE_NODE: return self._visitEntityReference(node) elif node.nodeType == Node.ENTITY_NODE: return self._visitEntity(node) elif node.nodeType == Node.PROCESSING_INSTRUCTION_NODE: return self._visitProcessingInstruction(node) elif node.nodeType == Node.COMMENT_NODE: return self._visitComment(node) elif node.nodeType == Node.DOCUMENT_NODE: return self._visitDocument(node) elif node.nodeType == Node.DOCUMENT_TYPE_NODE: return self._visitDocumentType(node) elif node.nodeType == Node.DOCUMENT_FRAGMENT_NODE: return self._visitDocumentFragment(node) elif node.nodeType == Node.NOTATION_NODE: return self._visitNotation(node) # It has a node type, but we don't know how to handle it raise ValueError("Unknown node type: %s" % repr(node)) def _visitNodeList(self, node, exclude=None): for curr in node: curr is not exclude and self._visit(curr) return def _visitNamedNodeMap(self, node): for item in list(node.values()): self._visit(item) return def _visitAttr(self, node): self._write(" " + node.name) value = node.value text = _translateCDATA(value, self.encoding) text, delimiter = _translateCDATAAttr(text) self.stream.write("=%s%s%s" % (delimiter, text, delimiter)) return def _visitProlog(self): self._write("" % (self.encoding or "utf-8")) self._inText = 0 return def _visitDocument(self, node): self._visitProlog() node.doctype and self._visitDocumentType(node.doctype) self._visitNodeList(node.childNodes, exclude=node.doctype) return def _visitDocumentFragment(self, node): self._visitNodeList(node.childNodes) return def _visitElement(self, node): self._tryIndent() self._write("<%s" % node.tagName) for attr in list(node.attributes.values()): self._visitAttr(attr) if len(node.childNodes): self._write(">") self._depth = self._depth + 1 self._visitNodeList(node.childNodes) self._depth = self._depth - 1 not (self._inText) and self._tryIndent() self._write("" % node.tagName) else: self._write("/>") self._inText = 0 return def _visitText(self, node): text = node.data if self._indent: text.strip() if text: text = _translateCDATA(text, self.encoding) self.stream.write(text) self._inText = 1 return def _visitDocumentType(self, doctype): if not doctype.systemId and not doctype.publicId: return self._tryIndent() self._write(" | | | # [a-zA-Z0-9] | [-'()+,./:=?;!*#@$_%] public = "'%s'" % doctype.publicId else: public = '"%s"' % doctype.publicId if doctype.publicId and doctype.systemId: self._write(" PUBLIC %s %s" % (public, system)) elif doctype.systemId: self._write(" SYSTEM %s" % system) if doctype.entities or doctype.notations: self._write(" [") self._depth = self._depth + 1 self._visitNamedNodeMap(doctype.entities) self._visitNamedNodeMap(doctype.notations) self._depth = self._depth - 1 self._tryIndent() self._write("]>") else: self._write(">") self._inText = 0 return def _visitEntity(self, node): """Visited from a NamedNodeMap in DocumentType""" self._tryIndent() self._write("") return def _visitNotation(self, node): """Visited from a NamedNodeMap in DocumentType""" self._tryIndent() self._write("") return def _visitCDATASection(self, node): self._tryIndent() self._write("" % (node.data)) self._inText = 0 return def _visitComment(self, node): self._tryIndent() self._write("" % (node.data)) self._inText = 0 return def _visitEntityReference(self, node): self._write("&%s;" % node.nodeName) self._inText = 1 return def _visitProcessingInstruction(self, node): self._tryIndent() self._write("" % (node.target, node.data)) self._inText = 0 return # pylint: disable=W0613 def _encodeText(text, encoding): """Safely encodes the passed-in text as a Unicode string, converting bytes to UTF-8 if necessary.""" if text is None: return text try: if isinstance(text, bytes): text = str(text, "utf-8") return text except UnicodeError: raise ValueError("Path could not be safely encoded as utf-8.") def _translateCDATAAttr(characters): """ Handles normalization and some intelligence about quoting. @copyright: This code, prior to customization, was part of the PyXML codebase, and before that was part of the 4DOM suite developed by Fourthought, Inc. It its original form, it was Copyright (c) 2000 Fourthought Inc, USA; All Rights Reserved. """ if not characters: return "", "'" if "'" in characters: delimiter = '"' new_chars = re.sub('"', """, characters) else: delimiter = "'" new_chars = re.sub("'", "'", characters) # FIXME: There's more to normalization # Convert attribute new-lines to character entity # characters is possibly shorter than new_chars (no entities) if "\n" in characters: new_chars = re.sub("\n", " ", new_chars) return new_chars, delimiter # Note: Unicode object only for now def _translateCDATA(characters, encoding="UTF-8", prev_chars="", markupSafe=0): """ @copyright: This code, prior to customization, was part of the PyXML codebase, and before that was part of the 4DOM suite developed by Fourthought, Inc. It its original form, it was Copyright (c) 2000 Fourthought Inc, USA; All Rights Reserved. """ CDATA_CHAR_PATTERN = re.compile("[&<]|]]>") CHAR_TO_ENTITY = {"&": "&", "<": "<", "]]>": "]]>"} ILLEGAL_LOW_CHARS = "[\x01-\x08\x0B-\x0C\x0E-\x1F]" ILLEGAL_HIGH_CHARS = "\xEF\xBF[\xBE\xBF]" XML_ILLEGAL_CHAR_PATTERN = re.compile("%s|%s" % (ILLEGAL_LOW_CHARS, ILLEGAL_HIGH_CHARS)) if not characters: return "" if not markupSafe: if CDATA_CHAR_PATTERN.search(characters): new_string = CDATA_CHAR_PATTERN.subn(lambda m, d=CHAR_TO_ENTITY: d[m.group()], characters)[0] else: new_string = characters if prev_chars[-2:] == "]]" and characters[0] == ">": new_string = ">" + new_string[1:] else: new_string = characters # Note: use decimal char entity rep because some browsers are broken # FIXME: This will bomb for high characters. Should, for instance, detect # The UTF-8 for 0xFFFE and put out ￾ if XML_ILLEGAL_CHAR_PATTERN.search(new_string): new_string = XML_ILLEGAL_CHAR_PATTERN.subn(lambda m: "&#%i;" % ord(m.group()), new_string)[0] new_string = _encodeText(new_string, encoding) return new_string ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/__init__.py0000644000000000000000000000143514567004737014432 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Provides package initialization. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Package initialization ######################################################################## """ This causes the test directory to be a package. """ __all__ = [] ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/amazons3.conf.10000644000000000000000000000007314567004737015770 0ustar00 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/amazons3.conf.20000644000000000000000000000052414567004737015772 0ustar00 Y mybucket encrypt 5368709120 2147483648 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/amazons3.conf.30000644000000000000000000000046014567004737015772 0ustar00 Y mybucket encrypt 2.5 GB 600 MB ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/capacity.conf.10000644000000000000000000000007314567004737016032 0ustar00 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/capacity.conf.20000644000000000000000000000025414567004737016034 0ustar00 63.2 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/capacity.conf.30000644000000000000000000000025214567004737016033 0ustar00 18 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/capacity.conf.40000644000000000000000000000025414567004737016036 0ustar00 1.25 KB ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.10000644000000000000000000000400414567004737015276 0ustar00 $Author: pronovic $ 1.3 Sample configuration tuesday /opt/backup/tmp backup backup /usr/bin/scp -1 -B /opt/backup/collect targz .cbignore /etc daily /var/log incr /opt weekly /opt/large /opt/backup /opt/tmp /opt/backup/staging machine1 local /opt/backup/collect machine2 remote backup /opt/backup/collect /opt/backup/staging /dev/cdrw 0,0,0 4 cdrw-74 Y /opt/backup/stage 5 /opt/backup/collect 0 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.100000644000000000000000000000162314567004737015362 0ustar00 /opt/backup/staging machine1-1 local /opt/backup/collect machine1-2 local /var/backup machine2 remote /backup/collect all machine3 remote someone scp -B /home/whatever/tmp ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.110000644000000000000000000000044314567004737015362 0ustar00 /opt/backup/staging cdrw-74 /dev/cdrw ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.120000644000000000000000000000136114567004737015363 0ustar00 /opt/backup/staging cdrw-74 cdwriter /dev/cdrw 0,0,0 4 Y Y Y Y 12 13 weekly 1.3 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.130000644000000000000000000000041114567004737015357 0ustar00 /opt/backup/stage 5 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.140000644000000000000000000000076514567004737015374 0ustar00 /opt/backup/stage 5 /opt/backup/collect 0 /home/backup/tmp 12 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.150000644000000000000000000001206714567004737015373 0ustar00 $Author: pronovic $ 1.3 Sample configuration Generated by hand. index example something.whatever example 102 bogus module something 350 tuesday /opt/backup/tmp backup group /usr/bin/scp -1 -B /usr/bin/ssh /usr/bin/cback collect, purge mkisofs /usr/bin/mkisofs svnlook /svnlook collect ls -l subversion mailx -S "hello" stage df -k /opt/backup/collect daily targz .cbignore /etc/cback.conf /etc/X11 .*tmp.* .*\.netscape\/.* /root /tmp 3 /ken 1 Y /var/log incr /etc incr tar .ignore /opt /opt/share large .*\.doc\.* backup .*\.xls\.* /opt/tmp /home/root/.profile /home/root/.kshrc weekly /home/root/.aliases daily tarbz2 /opt/backup/staging machine1-1 local /opt/backup/collect machine1-2 local /var/backup machine2 remote /backup/collect all machine3 remote someone scp -B /home/whatever/tmp /opt/backup/staging cdrw-74 cdwriter /dev/cdrw 4 Y Y Y Y weekly 1.3 /opt/backup/stage 5 /opt/backup/collect 0 /home/backup/tmp 12 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.160000644000000000000000000000052414567004737015367 0ustar00 example something.whatever example 1 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.170000644000000000000000000000061414567004737015370 0ustar00 /opt/backup/collect daily tar .ignore /etc ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.180000644000000000000000000000060614567004737015372 0ustar00 index example something.whatever example 1 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.190000644000000000000000000000356014567004737015375 0ustar00 dependency sysinfo CedarBackup3.extend.sysinfo executeAction mysql CedarBackup3.extend.mysql executeAction postgresql CedarBackup3.extend.postgresql executeAction one subversion CedarBackup3.extend.subversion executeAction one mbox CedarBackup3.extend.mbox executeAction one one encrypt CedarBackup3.extend.encrypt executeAction a,b,c,d one, two,three, four , five , six, seven,,eight , amazons3 CedarBackup3.extend.amazons3 executeAction ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.20000644000000000000000000000007314567004737015301 0ustar00 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.200000644000000000000000000001222714567004737015365 0ustar00 $Author: pronovic $ 1.3 Sample configuration Generated by hand. dependency example something.whatever example bogus module something a, b,c one tuesday /opt/backup/tmp backup group /usr/bin/scp -1 -B /usr/bin/ssh /usr/bin/cback collect, purge mkisofs /usr/bin/mkisofs svnlook /svnlook collect ls -l subversion mailx -S "hello" stage df -k /opt/backup/collect daily targz .cbignore /etc/cback.conf /etc/X11 .*tmp.* .*\.netscape\/.* /root /tmp 3 /ken 1 Y /var/log incr /etc incr tar .ignore /opt /opt/share large .*\.doc\.* backup .*\.xls\.* /opt/tmp /home/root/.profile /home/root/.kshrc weekly /home/root/.aliases daily tarbz2 /opt/backup/staging machine1-1 local /opt/backup/collect machine1-2 local /var/backup machine2 remote /backup/collect all machine3 remote someone scp -B /home/whatever/tmp /opt/backup/staging dvd+rw dvdwriter /dev/cdrw 1 Y Y Y Y weekly 1.3 /opt/backup/stage 5 /opt/backup/collect 0 /home/backup/tmp 12 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.210000644000000000000000000001333014567004737015362 0ustar00 $Author: pronovic $ 1.3 Sample configuration Generated by hand. dependency example something.whatever example bogus module something a, b,c one tuesday /opt/backup/tmp backup group /usr/bin/scp -1 -B /usr/bin/ssh /usr/bin/cback collect, purge mkisofs /usr/bin/mkisofs svnlook /svnlook collect ls -l subversion mailx -S "hello" stage df -k machine1-1 local /opt/backup/collect machine1-2 local /var/backup machine2 remote /backup/collect all machine3 remote someone scp -B /home/whatever/tmp machine4 remote someone scp -B ssh cback Y /aa machine5 remote N collect, purge /bb /opt/backup/collect daily targz .cbignore /etc/cback.conf /etc/X11 .*tmp.* .*\.netscape\/.* /root /tmp 3 /ken 1 Y /var/log incr /etc incr tar .ignore /opt /opt/share large .*\.doc\.* backup .*\.xls\.* /opt/tmp /home/root/.profile /home/root/.kshrc weekly /home/root/.aliases daily tarbz2 /opt/backup/staging /opt/backup/staging dvd+rw dvdwriter /dev/cdrw 1 Y Y Y Y weekly 1.3 /opt/backup/stage 5 /opt/backup/collect 0 /home/backup/tmp 12 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.220000644000000000000000000000045114567004737015363 0ustar00 machine2 remote /opt/backup/collect ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.230000644000000000000000000000260014567004737015362 0ustar00 machine1-1 local /opt/backup/collect machine1-2 local /var/backup machine2 remote /backup/collect all machine3 remote someone scp -B /home/whatever/tmp machine4 remote someone scp -B ssh cback Y /aa machine5 remote N collect, purge /bb ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.30000644000000000000000000000024614567004737015304 0ustar00 ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.40000644000000000000000000000054214567004737015304 0ustar00 $Author: pronovic $ 1.3 Sample configuration Generated by hand. ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.50000644000000000000000000000060214567004737015302 0ustar00 tuesday /opt/backup/tmp backup group /usr/bin/scp -1 -B ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.60000644000000000000000000000176214567004737015313 0ustar00 tuesday /opt/backup/tmp backup group /usr/bin/scp -1 -B /usr/bin/ssh /usr/bin/cback collect, purge mkisofs /usr/bin/mkisofs svnlook /svnlook collect ls -l stage df -k ././@PaxHeader0000000000000000000000000000003300000000000010211 xustar0027 mtime=1708919263.148956 cedar_backup3-3.8.1/tests/data/cback.conf.70000644000000000000000000000061214567004737015305 0ustar00 /opt/backup/collect daily tar .ignore /etc ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/cback.conf.80000644000000000000000000000372514567004737015316 0ustar00 /opt/backup/collect daily targz .cbignore /etc/cback.conf /etc/X11 .*tmp.* .*\.netscape\/.* /root 1 /tmp 3 /ken 1 Y /var/log incr /etc incr tar .ignore /opt /opt/share large .*\.doc\.* backup .*\.xls\.* /opt/tmp /home/root/.profile /home/root/.kshrc weekly /home/root/.aliases daily tarbz2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/cback.conf.90000644000000000000000000000053614567004737015314 0ustar00 /opt/backup/staging machine2 remote /opt/backup/collect ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/encrypt.conf.10000644000000000000000000000007314567004737015721 0ustar00 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/encrypt.conf.20000644000000000000000000000027414567004737015725 0ustar00 gpg Backup User ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/lotsoflines.py0000644000000000000000000000113514567004737016142 0ustar00# Generates 100,000 lines of output (about 4 MB of data). # The first argument says where to put the lines. # "stdout" goes to stdout # "stderr" goes to stdrer # "both" duplicates the line to both stdout and stderr import sys where = "both" if len(sys.argv) > 1: where = sys.argv[1] for i in range(1, 100000 + 1): if where == "both": sys.stdout.write("This is line %d.\n" % i) sys.stderr.write("This is line %d.\n" % i) elif where == "stdout": sys.stdout.write("This is line %d.\n" % i) elif where == "stderr": sys.stderr.write("This is line %d.\n" % i) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mbox.conf.10000644000000000000000000000007314567004737015202 0ustar00 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mbox.conf.20000644000000000000000000000061514567004737015205 0ustar00 daily gzip /home/joebob/mail/cedar-backup-users /home/billiejoe/mail ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mbox.conf.30000644000000000000000000000076014567004737015207 0ustar00 /home/joebob/mail/cedar-backup-users daily gzip /home/billiejoe/mail weekly bzip2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mbox.conf.40000644000000000000000000000251314567004737015206 0ustar00 incr none /home/jimbo/mail/cedar-backup-users /home/joebob/mail/cedar-backup-users daily gzip /home/frank/mail/cedar-backup-users /home/jimbob/mail bzip2 logomachy-devel /home/billiejoe/mail weekly bzip2 .*SPAM.* /home/billybob/mail debian-devel debian-python .*SPAM.* .*JUNK.* ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mysql.conf.10000644000000000000000000000007314567004737015402 0ustar00 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mysql.conf.20000644000000000000000000000040714567004737015404 0ustar00 user password none Y ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mysql.conf.30000644000000000000000000000045714567004737015412 0ustar00 user password gzip N database ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mysql.conf.40000644000000000000000000000053214567004737015405 0ustar00 user password bzip2 N database1 database2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/mysql.conf.50000644000000000000000000000046314567004737015411 0ustar00 bzip2 N database1 database2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/postgresql.conf.10000644000000000000000000000007314567004737016440 0ustar00 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/postgresql.conf.20000644000000000000000000000036214567004737016442 0ustar00 user none Y ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/postgresql.conf.30000644000000000000000000000043214567004737016441 0ustar00 user gzip N database ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/postgresql.conf.40000644000000000000000000000050514567004737016443 0ustar00 user bzip2 N database1 database2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/postgresql.conf.50000644000000000000000000000046614567004737016452 0ustar00 bzip2 N database1 database2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/split.conf.10000644000000000000000000000007314567004737015370 0ustar00 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/split.conf.20000644000000000000000000000025214567004737015370 0ustar00 12345 67890.0 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/split.conf.30000644000000000000000000000025214567004737015371 0ustar00 1.25 KB 0.6KB ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/split.conf.40000644000000000000000000000026414567004737015375 0ustar00 1.25 MB 0.6 MB ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/split.conf.50000644000000000000000000000025314567004737015374 0ustar00 1.25 GB 0.6 GB ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/subversion.conf.10000644000000000000000000000007314567004737016434 0ustar00 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/subversion.conf.20000644000000000000000000000046714567004737016444 0ustar00 daily gzip /opt/public/svn/software ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/subversion.conf.30000644000000000000000000000047714567004737016446 0ustar00 /opt/public/svn/software daily gzip ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/subversion.conf.40000644000000000000000000000150614567004737016441 0ustar00 daily gzip /opt/public/svn/one BDB /opt/public/svn/two weekly /opt/public/svn/three bzip2 FSFS /opt/public/svn/four incr bzip2 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/subversion.conf.50000644000000000000000000000050314567004737016436 0ustar00 daily gzip /opt/public/svn/software ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/subversion.conf.60000644000000000000000000000051314567004737016440 0ustar00 /opt/public/svn/software daily gzip ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/subversion.conf.70000644000000000000000000000233314567004737016443 0ustar00 daily gzip /opt/public/svn/one BDB /opt/public/svn/two weekly software /opt/public/svn/three bzip2 .*software.* FSFS /opt/public/svn/four incr bzip2 cedar banner .*software.* .*database.* ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree1.ini0000644000000000000000000000040714567004737014751 0ustar00; Single-depth directory containing only small files [names] dirprefix = dir fileprefix = file linkprefix = link [sizes] maxdepth = 1 mindirs = 0 maxdirs = 0 minfiles = 1 maxfiles = 10 minlinks = 0 maxlinks = 0 minsize = 0 maxsize = 500 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree1.tar.gz0000644000000000000000000000177514567004737015410 0ustar00‹ő;AíÉ’ŞH†kÍSÔt&$ÓRdVÜÉŚ‚Ě>}WÝîatĹm{cUW\ľÍŮüpN|™Ů·q ˙xy*ś¦É÷ iĽWúQ˙ć(ś@8$^$ /Żäs?ë/†®?´ŻŻ/u[ť«1ý\Üv˙ňžź?ňł~úóOň"~›É“2ŢűAQč—ó'hüóG&^^?Ą‰żůü­˛0ŤiŔn/ŐĆPSó u¨|ˇág]ě–Ľ0vľ‡ałÇ|á0lłć§8Wßđ…Ó¤[AVxÉlÁ 5ÔxĎ`ă ’ĽÚëy!§oŐžf»}áVŞ%šô"•tÄ%;Ůúµe¨‰Đ4 ¸Őľ|,Ť2ŰŢĐÔ.85TIâĚţĚ›k×°´KżŇÉ«g¨ŚßpąS™M—@±đsš’EŘŃuĚr´śÇSçÚÝ´&Ɔ şµHg”ĽMŠ<Ż8ËAŘWwţ˙Á˝˙ř“2úŹSł˙_¶‰ŔrŃQŠ»·)aHjkr=ŮÚr067 #zů•öÎ?Ä2[GŠśyră-Ť0Á§ 6°çŠmŰeé¤8\€Č2Ej"ůy't#·®ĹV™˛ئlH2Klj)^®©žIŇ9ôâF ÷»Ş^%Án®*ZßkŰTŤ„ĺąX[¤{ËńmČ&-Ć-8Ýigĺ¶"KÇśÇ1IŰ>«”JšÝţĎÜűO<)ă‘˙8 ?úg˙?QżŕľŘ5`Ł\ŮBâ‘PxŚď'ík•Ó0ɶwY·0cçNË…¸\-ÍˇŚ”öboíSéj:í8Ú–a’]!ËčS¨ítÍ3őĐŢ–„s6Ť|ëŽ5±KŁ5cZŮ2‹DścSfŔśŢ­hř˛>ZFßINËÎ{+ž.B5+ýîýGOĘxä?ĉŹţŮ˙Ď`oN<]eâit#ćGŚ%řzÆň˘ééx´şzşŐ×8Żm‘‘4·»ŚSŚş$´ Tví]ł˘Í@'RłOs6ő;qď˙łî[î˙ţč?9ű˙(MGü2hžŻ§lFśĚ @Á•ÜÚ´K?šT[ĺ=:)©8,TeĹ•¸-q[Ąu‚ˇC4fäůi´|ęvŰó)sc†ßxĐćWksoű±ô9ĺĘßD®}íi2U˘#ż }mŮ~Í5LrX_ Á‘1 çµăłą÷źzRĆĂý˙íä1ź˙ż†ôĐڰł Bóši*Ą[t¤َŘäL.’(Řşе¶nI2$u Z>&jh1ŞZ,ěx–ösď?ý¤ŚGţżŻ<óýß×@ű›A™ ž™™™™™™™™™ů-řQžđ+(././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree10.tar.gz0000644000000000000000000000060714567004737015461 0ustar00‹çAíÖËn‚@`Ö>oĐą2˛äVĄ±©4Dmw­ ÉEhbźľ ¸čÂĆ.Ʀő|›aA`8ÎęJJŚn ťBDŢ­XpÔ­3¶_{F aQĘ…Ń^Ě “kÝUŻŮÖ/•iëŞ\•ďjyú>YmżyÎńCŽëQň/Ô*oSŇóŽ®B3ó§mţśZÂ0IŞ ©mW=ČżË?QU[hMÇŔOúźu%ÄŚ ý }ţ‡NĂzŢŃŐòŘÉü Ą‡ü9Ă}ţí1`^¤Wž? âw7q¦#y[‹ćiRecä¬C9J|žż.Jžâ %»bjŰ”yÔŃr¸)íĐŤř,T;ßWŮ0uJ”Żća„Ił-śÇMâř~ž&RfX‘|IĐL Ľ sݱç§ö›˘ĎĄLµăA’Ą‘5¨bc7µĘĹć.`Íŕ·KsľĚŤýţüďţ˙8ˇ¨ť˙ű©¤iSGWŢ˙€˙ăSŞPű(././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree11.tar.gz0000644000000000000000000000120114567004737015451 0ustar00‹ß:‰AíÚËnÚ@`Ży żAgÎÜÂ’[*ކ ‘´»lŐ@11¸}úŽÁ¤ „+Ź«Č˙·qäD±3'˙ĎMśżó\bŚ‘1*;rŁXvd\ĘÝ1çq{†´ŇÂc\#ĎWNď*—®7O‰ď{«$^Ć?ŁÉůź ’ő…ßsřCÇ7błŻ˙"ZÎmQÜ\#cĚő’3{ž+!•çS-gw•CýłúOŁÄ†ÔQ¸>˙B ˛˙'öK{ ůŻŔiýÝ.­ĺŮú“/ý_3•ĺź”öüJ±ćő—˝Ńďöx”ŽRvĽßôË ™Ý±ÖŞÜN»jţí1V!Ú.î›M!;˘c†“›ç¸ŮoŐ¸m»Ýhv¶b6_>ô‡śŇő˘ő9fOŁŃLJp3Ń|Bl5:˝Y»}×é†Í‘řamUo: ‡ş‘´¨ÉۡŽź?ôdÚřßCS ŻĺźJľF±üë]ţ5Cţ«€ü×Űků%_ŁPţ9ŰĎ˙ůŻň_oyţť®´ŻĚż0JrN´[˙!˙Ő@ţëíd˙Żě‰?—ĺˇŘţźáŮó?٧˙W´ůîŻWO“ŕRúţ]ÍóÔ˙ť uç?ed¶ţÓ$úĐ˙ëíďúď8ţĺľ`Ţ˙iĂłüs%°˙_…3ő/u=PhýOYýŤť Đ˙«€ţ_ogňż_”ô" đóżmvŘż˙w˝¨yţ/ô˙ŇFľP˙—öűÄwď˙Ń˙ÝC˙Ż· ýżÔüî˙ ź˙ŞÂŃţźÓýźbőç˙+r˛˙[ö?r…ëĎ1˝ß˙uvS5Ż?ĽmE`ŽP././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree12.tar.gz0000644000000000000000000022266114567004737015471 0ustar00‹ĽşBěĽstŻĎł.Ű;¶m›;¶m۶íěhǶmsǶm;™ěď9ż™sĎ­™ąwÖ¬ąýOWwWWU×SUýv˛ÖÇÉÁŘ‘ŕże٧§gdccůŰ3°±Đ˙íé™˙é˙˝0Đ3Ňł10±0˛~Ď301Ň3ŕłü7µęß›łŁ“ľ>>€ť­Ť­‹ąá˙6ź±ă˙Žśä_ý˙GšÓżáďlcnhkdüß&ţÁźĺ˙$ţLńgfedúř˙÷h˙ aźŇ´ŽNú6¦Ć46úÖĆ˙÷uüőëżáýż†? Ó‰?ă7?ţ'ţ˙%3c|s+c|Ws'3|}üG˙/ňřć6řNfćŽřFćƆN¶îřß[|kóo§YÓB‰;á;šŮş:~łé;áK™Ű8»á›éŰY;ţ#őŻÇżb”˙-ľđÉ••DhŘ) ţjłuvÂ7v1vŔw0Ö·2÷0·1ý71NfĆîřúĆ˙a-”¦­ë_fj|GŰoËl­Śđżq#sü‡ŃŮĆLßÎÎýßÎđŹĹ˙RN« %n‚ďnëŚďŞoó-ŰßŃř[˛>ąÓżźüź“ÚÚ8~ĘßÖßÉőű„ÎŽć†úV߇u2v¤ŕ„"ů‡ĂÖĘŘĐLßÁź†ĆČŘDßŮʉĆÄÖĆ ’ßĘVß_N˙ďßâ;¨ţŞ20Ćwtv0ţŢý˝ĎźĆźL‹ž‰‰„ťě{‹Ł±Ó˙ě–oWŘ}{Ăú›ţf¶úV  ů—çŰą˙Á‘äú®˙•4ţI“Ňw2·ařŹÂđőMőÍmh˙+8[ŰşüuĂß“9XăÓŇńýÓţ+Fs›ż˘ţÝ̖̿Fş˙H¦qţk ŕ·ëľ0q¶"Ŕ˙iűoľ·µ±r˙W8ý‹ęźă8ŘZ˙ť˘ˇ§eg§ýĆŹÖÔßÖßJßÉŘę;pđmŚÝľýăRKcwG|׿AńM}C`m`nó}Čo(ľĆŽv¶ß{ľít5374ú—;]ô­śŤińÉż±Ö·rükŘ?;Űü;Ćeţ 俦Ą€‚ú/¸ Ěíľ™˙˝2ęţ-NřMü_flíţĆ(--ťŁ!-Ô˙Ű©ü©ý§úż›V¶›Vń˙°Ž˙úOĎĚĚöźîV¶ďĺ˙Q˙˙;4“ďÔű/óúĄ\ý-J˙žŽPÓČÁÖę»6™›ü“—˙¤ >ľ2#+«ţ˙+ýż8„ H ÄńťÜ팍ţ—jýOj}gŁŐ÷äwy6p˙' żk1ŤŁ»µÁ·´ż%ĘĐÉÁŠćą4" ř˙qÄř·ĘŮ|WŁďĺ»"üSCô¬ÉţÖđ/Ćú&úćFÔř¦ć˙ż4čŃČá›Xé›ŇB‰ţťÖűľ»ľ7ýŰ’Ăż–ţ߆éżYűOůďědbő}ŇZŮÚ~ç˙gtüďć?33ËĘ6V¦˙ńý÷ߥáEB@t·#ł˙Gč2>Ě]‰‡X"Ľ}}Aąďjm×,rëËř)kBlšŞ'žÜÝHşJ–’ö=ťy*†¸ŮK±řcS‹m`6R=iÂŮą± Ä|(|k—nwuí˛:™ęÂF¨Ĺ$7"µMłĂFŘß~f„±ÇżDę*}ĺE|Q5`ßń˝ď˘-¨VúľĂWđZŽFÍX ϵZ)—ÇŰ"Ş7>Ľ}MżŢYP¨ŢKZTč~‰.f[•˛č–CťUĽ{MĽžřZ}0bäż;V>Ě} ĽÎ™&š4ę~<ň5~˘W&č)Ţv>ôIůXŤŁDĽhi2ĄżÔ=ŚK.fG­¨UX0ÝŞ0Lh#¨>Ľu«ůŢžĆM×€|9RTÉ1ßťid×|~tÇ=I€63ź…~ő˝&ţilÂĽ{F9“ul_9˙µčÔ„$}ÖüUřP\›4Ş„ŃĘúR;óşăKň‘>^ Řł ¦:ž­ÔFˇ]á±ńçh_őäŞü˛şµ‚[;ÚÉ©ńÍđ,nvĺď÷iÍěđŇz6–caÇ;Ą¶–ć'‹7ŽÚÉ6çÓ’ŕ+Ë‹<Ú7?9ňm)<×·<9ňî»ßR|8XěVĽ9ĽüR§Ak®F{űŇ]p°ő¨ýâ˛hmńę\ţz˛hEhŐ9˙oÔzVĄ{üĘ× ťÔůî»KvO ľľThĐD˙ntýŢŕţ˝á[3űßľ[u|pđlđóËux0±Đ«hcŻčÁŕă+FŐ¤+Ufěßµ­ţ‹Oű î$|áK—Q©‰˘Ë`o+1ß•D„ĆfÚ" ôËKפľBɰHi§śŔĺý[ `ńíN( ˡĐjěC=ęńHö €qĺRb@ËW'9wĆ)QČęţ%÷¶őě)`ž~€ŮWG;é§&vÇňţ”eX> “-xqÚîÔ„öęę…ý” Ń»»PU^BM9eŠla˝ŠoQ=Eąn$‡yÓó]‘u•u«Óbťz"Š_ †ĺźqVußĐ>׿Şhś¬[Ej§˛›ű5\Śç©ş#ő?;Ú)häÔ/Yěů®Ćślď ^‘č× Ýć ö¤ŔÓý&˛–ĺŮňÍUÁ$/ÄHÔ!‰ďNXÉ`dztŢŇp`Éôš€Ăď óLăd{,Bgë›I—/J3k}şw ôZM{ŠTŠ ČÁ\¸€BĚ-剺{-†ýQd{¬ČŽăë’fVhŰ397ĺJwč×ÓÜT)ćŚ*k0iľ{Ęş$L¤Ćąi¦XZLîÄ€B:ĺü¦ŰeěçAÎß×ĎězÉą)\ľĆĄ­´ť‰´­•¨Íôűu9 ¦kJs‚ń|O˘Rö€äWß ©D1,󻝆s:«~#ű|’‰Ń‘˛ qđÖŰ]Ńuj č…řžĚU±ť“9·¦"ű|ę¶7AÜ·Ń?Ń35/ŹűbD Ľyj›}¤ä˘ŁčňAĺ1Vh'ŕň»}¤ÄŁ7Ľ^wŻGĽáń‡}¦Ě Ł\ôĽ1\i^§Ě~˝Únş˝mmľ.ů"‚Ęůjµ·Ü01žĘšˇq~~Šű?$2ű*č}(Á¬.ńQLűpŠ ZsŁóý|| }ćŁÚ3ĄP”j66¸˙“OqíM[ôs…ÍB˙Ůýb–rÚ˙ý˝aÍsű5˝ˇű‰˙1řŰ@_żëČUőqü÷—Ę•ŚqÓ@+ť+‹Ľ˘-“J] Ź»Ě­®›¦mééŹ,“‡ý+ŮRÝxáÎé6Gđ„µ]]̡ńPpđŇňÄ‹kg¤j]ŘáO°¤a]yň@: ýméŐć‹aţ:`ĂKDµ6t;Xńjľtűżăˇş˛€ű›™#Çůű{/.Ä*Öěč=ÉÎlŮÝ:±J·­ÝÜ>Ôîrr:ç}ňÔ ^W– ,®ĚQ+>yÍAĘźje˛™ŔăŐtżűĹoSlň{Ź‹ż‡_ţ[S5=­Y´n!ç"v/äuÄ®=6 i±,‘{üşţČâŐš«{öćÄ*ş«ů÷ëĹ@·î˙Ą+;ç0ŕß›„ň3±ü™"‚~b><Ą ús54¸;™pńéňÎŐ¸¦ćeŁEŤ›ŕÍ3•·{ E˝!…· -č(˝‚D*˛'xâęń'Ş6tćÖEtxą5°öGpÜËF×Hďę TçÖElä2ĎzŰ+4ÁK;𦣿¬@ę神ÎbÄBZ\Ő§ŕó祜 oÇôÇĆNK1˝ÄĘN¸8•‘Ż*VP1™ţk{~ú„"ß#Ą/ŞbSV'Tr¬č·ĘŔ ĐKU‡‰ńsF0Y˛InĘÓ^śufhĐÉS4ÝÜ.ÝPES"SW:ÜËĽhŠg.Ş˝÷/#»ăz5ď.ësµĽč>dśIö5zͬ—6 -ëĘr^ňőăU S)§v4 Y[ŃÔ›˝»>7<H<+„>\ŻD“/ű/ŕw_‡!e«wźż6Bg2EŻbČ#&ő´§h•VĚď´±%ŔljGSwĂ€-<‚ś÷śPÖ!njÇÂpkSŁw#*ÖqŮáŰîlLŚäŤëŮůPYĄćx:Q_ôŢg¦&h7‹X ´ĎÄ·@‹Ě´Çî=ęrŻ ¶§‹­I\úc[j ' ¶{îäÍI‹Ô&±•ĎVC@ÍĂnűŔ@xçđŞ^˙xÁ¸˛´-‚řPňśĆub4rîx“r¤D śMOí ±UŁútÝôşłfęŚ`ŽsŤm,~ýL…8!¸˛ ĽĂG-ŕŤ¦X™sş~Ą®ł‚ÇěૡÚČ"‹ó˝˝vZCmđÁU¬{˘ßˇDRŢlÇPďĐhůЕ¹ä¶J~â$§ >ľr´âTL÷Ü?ÓFŁŇň­&+ÚŻ‹Ž{żŐ±5ZÁm@řčěj™µ¦" 8ěü<®Ă©°{ďÄi˘±Ő¨¤WTwѵYU&žč(Ţ"›bUîTrŰk¶ Ç#đ>ä‘Öş…šÚŠY9q őÜźď`CjŁ >şÚ XĂ¸Š·¸Ű43šCg»ŠĽx^ ŐbŐŹ ˝Űt»×âUĘž†?äČéąßĐ“I­Śşó±!R>â‘fĆXbb II‹FÎz2!Ěé=oý¶Ń†¨ń¨óĺŰFęçŠč~h˙b|{7hş¤óßŕń(÷›F7> vžU1|®ůTv3#W:Z'.˛„sÇťo3Ć\jôXhB4€Í, nč°É|TćĐßEE»)t\Ő@ÂĄ‡®łSÝ &¦z0#…}†ů6}S3v˝Î±vűYDjôŇ/<©ř‰•(’6°÷¶2µ.ńÇŠÜ•µ(ę…AďĹwät’ýPŤ§~Ř´1şá5’í|bżŚăĂ&*<í$›i/Dąö¦+ÂuŐ÷+(z$˛$ěu±öób°Â§8ă‘ö«R¸ĘĘřó˘ÜóażÖâzýE^w5IçĐľ97tRý6lhäŃY>¨ÖŇc.5şů ±w“+’z#4”,cx~;ŠkÍóŠ°Î šlú<ŰÔő­đJő°»rę‡ţűŰęôzŰ‘CßpJđŮ•ë_d+«QĽ¶·zË§ŹŞ‘ĐŢÎëŰŞö]ń;ţÍ×é¸ŕÍ ÖˇNý›÷…Ž{Őĉ¬…”ŕÉ«á2vm|%źn>‡ńe¨Đ­%“Rôč§Ů Ę‹oŽž+ř!+@®¨Ŕ-ĽŔ.•ă·O»}{ ęuoÝ>š˝&‹l¤äwS‚[Ż,ómą8•AÓ!·–ó‘&Ř FŔä\©gŤ´€ŐĘ'xĚďTŐ¸rĹ> ő.ʦÂţ‡˛ ş$~˛­bő«J—kiĂçW˛öËśĚmÇâź[IĄ!YŞĹâźoMŤF;’ç_xŃF‡*ŰŚŃĘH”„Ý.%Í›DŻ OB´b*)‹.·ţl„BâÖŽRç!Ş]2g[nCn B»_Z!–ČąŘh¤zâĚH…ěľÚŠ”OŘ“ }"ZŠ‚Cć^YäXÁţ©rý–n–QŁ‚%Đüť±óÄč…”˛gjjFŠźůü±0R‹Ł^§Żűţ©1°lłÇżě|ˇ#ś«Îźs*R#~ŕ_ŠOšâi«ďűţibÄľe˘ĆXwä/°X™  ¦ŰL4îš*#Őę>瀹Žc?Ďż°‘Ę>_„”đ{Ë#Sá>Ľi˝IłyżŠđĺĹŰßkă)n$ÄÇ—Ź”Đ©'ç\Ŕ¨ă„_`ą?uyÇ.Näăsnd»,2Űśă©Í.|ń OŘOë“y± ¤Vď.wşbĹşt«ÍŘ~„Bč:,+8µ®RçLJŰ[ŕoó±?öŁRśŘü2Źŕ8J"6ú}KđśôDŽF uŃČ”>r¨¸#Řᎂ‹đĚ–•đSEŔyě‡?lj |sVěŕ”ÉÎ?ř±Löů!Ô—ńî‡/ćŔi´…9ÁÓaMQ©×ż·LüÇ|č‘™ C©Đ€|s mŐk®ă[*<‹ł:đÖ#d¶ťjúáwë*p+řĐ#'?k¶ŻÝ´$ęÁ~‘ĺ1cSsc¤\žĺôž”{1C ĘŞ¦Łh)L¨ůŇ+ŞgiđŁüÉęTęś´¸­áKm˙ŮXE*Ćç[ޱ‘ŔµŢőł˛Š4c˛Đ˘ń¦šĐň-0XrţÔ˘/•H5¸§×5™˛Ó‰ž(ăÄĆxtÂG™ŘŃ׎®JÇ4–4F5rňr±´ď ÷ضÄE)ţěĂźv‰+(äŠ'B=ŞM‘,ˇ“O-FĘ„"pdžĘ>Ť‘ő,îţŁ×Â)+*0X´rç™ËĚmĆăŢľ;0xÉj *üňBˇ‹o9 y\ďyHEŘś`ką*'Ý×ËŚRgÝŤôÝqZ řkŰŢ)+$0{f&p" ˘»:R0oaĂ)ţĺŁWźQ›úŘAáf‚uë8†ű ^†ŞcGô×ä.(Üg†h,$-zAkÉńj\·ĽŢÔTÂR§Ĺ‚XĎçŮIJ? ᣕ|4óy·ËL‰Jî’î^BíhwŚRtĹy·©qčŻ {ß‹iÚ6ą°§Ą¬Ôw…îZß‹čR˛un ËŻU4uE5q°ŤîÇá˘ß/Xä†WşŕyŐÉ JĹźśčО-î»Ňľ8Ä>÷K;c ôë`lÎ Č0ŞÂ^§ŐöâÄ;‡á˘,śż‰ ‚”®şc‰ą@0Ă‚ľ)ŃCnĺmŮ AżÁ}ÓŁľĄ€ř¦—ťç,Xăż›·%ި‚~2˙ř«ĺşxe$]PBěëĹŽšq›„¬čEΓhn&}Ż?ü¸»Bm»t„ÉĆY™hçHhböÇz™HŢTčÜŚ*¶ÄKa _F8/yߣ‹‚oFtçDRî‰ĐÔ¸§Rôz[âĹÂ…;µ Ě4n<‹ËşĂčĹ€N@íhâ,ř˘Ťbu»ś+ #ÚŽ»śjZkťY4o3z7‡GĘ'.± ÉßĂâ:, .Ľëa—# ˝›HÖýĹÄ^-(Ţܦ&@ ŃÍDą˝†!—^%­=齸n,ÎĽëŃ jĺÁB“ˇ±ůöY‰ŁĘD˘GBŇ12ĺS#/vtŻŞ˝DÔ˘÷3ŕźnrq&CÁg‘ ‰XľąK˘żŕÍĚhDDtÔ_<*1iĘ‚dĎë…Ę‘µŻ1AJ€ă`Ç;_0ÖĚ˝çt·c_Ŕsě+›gŻßî˙§ř—4m¶pŃ&TĎŃťX Ź/ÁÉa¤™aŰô93H.ÓvčC\řÝĚ„™¤D,!ôQ~L*bo¸BÁüg #ÉśypÍ+Ń%$Yá‚X×KÉ’"Lr¸ ¨ŕy,s$‘ű—öŚë!%¦ŕM¬GŁôí ÉČÔK ];cËQţçÓbܡřQ> ¶[Ô·gŽĽK9ČrÄţĽŐ˙i˝l—{Ëß1mŁeßąťyag! úcďOúě˝đ¤5Ňýrěl „;kÇë‘3A®šŹgoĄhnY»FÚFťË!šC–ŢžűŘűÄN“ ‘Ááq(ž»FÍ–Y«›„đX-RÂŇÝ=”§@±8Ceń$xŹ sěhËĹFë‡~K»š·Qi+B‚+c(%š¶Î: IďŮťŔ×á‰u_S˘Aťl§×¸Ň<°á¦l§;VxK;Î7ńfŮĂ({öŕŹ‚a˛†Ô¬#1őŔK˛ŢnżŻ:nŞ»_`Rh‰gĂď·v*ćĎËľ~8°]ÎkÚľĘúşť-yéťÜË‚y©Ý:čď¦dĂĽčŔWöŽ“ ů/’c×€ çfĂĽq-ŐĽ g<%"f˙a‹fď»qµýş|2kmą#kVz D0‰’Q†úl-3ÍČŽĹL—>¨üeÍŞŚ‚Îr)»maô2”Mśĺ6kŤGĂ—vż¤*˘Ô9˙řÖěCTMXŇ+~@¦čců,(!Jň0t%nžˇčĂXIĂ=55>żmw›ŃÉG"5 ń:qÚ9 ęČ‹OĘ’T24Z¦µ'_ˇl[îăĺ¤ďO?TižĄŘÍŢź U/ßV.Hrużőkv{÷Gů’î _ÎŮ ]ÜŔ-ó^ŘűÂ?1ÂÍó^8#ö‚HÓ9Ty¬aîÎg?¶bаŞ*33™hBczŻVSMîÇ«ćđŰÉ‚7pęśńâĚď¨ćŕËĽťÔȄ€`‘||ťĐdĘżŕď6fÇśž1ü˘é×_)6¬!?éďěÔű- '«mĽs_Ľ_ô#őůŞř§Z5ÉćŰIśqu—ĄťĆăIśÉÖuuŻýJń3ţ1[>p— \Ňďh˝0?ţNś’e«ţž¦ňG(ěöJˇFtkkżŃ¨›jÆ"`@4§őµíçł9aRh±Ö‚ţR`đÉ6ŢN,!č°ú*Ů«?kt¸VŢÎęţýߎ@@θ§ü‹§ŰF§s˘oč·Aě ŻNC »fš[Q‡lĺśr ř×&Ňš €ř5&<łŤ `«VđŇU˘‘Hb ę3r{sŔĺMóˇŰCĘ·˛Í n#QXŮ—fĚýÔ´ ľmXl©v[ŢŻj°Ş| ¶Ďý+›Ĺ˛„¸şüčďŹóřžÇýń›ĹoQL×;RWsŔ,#ŹvéO'úFđĐ0ëŢe!+B¸1gĽŢEţ~-˘aLë;-—s†ťň Ŕ‹Y% “rˇým˛˛ ř¦9ýŦ!3ŢU"zqý3ëĹh !ěŘ#ŢŻZŃäđvěQoçE!;°ĹvŮÎ%ŘëvçŹęĹłaSéŔŘ1Ľg BÚq×Çő6Ń3ô`»őYbĂ©5Çő:{úfý3«¦Ţ˘|Eދՠ†x13ĽXÓÎl˘ĽŘżĽYŐ쉉(űJ¦Bh™Â’ĺÎëwÄöŢŔ13˝YxŞBä®ë5eχ÷rÎęĐÉ€%•s;YâÇĂ©©†Íí¤cŃa¨†M»PőűK`@űIä<™¦'«îČýí-Hľ:—ýÝÔŕv©eÜôt9ě,ďĆz‚®÷IWxýň ¦Pn•V1¬ěMîW¦öä‚ňó¦.ÇŘ4vŘ?^˘4í¬úaÄŇĄ{¦ÓŕÇkzR˛!Źxf“Ä=,Ô“˘B+Í$-j©‚Ć«,"říVY¬Ő'ZąőěÖťĄŐá †V§“ŚŚ~×4ô7.O±˛ţҧżTůyU¬o¨ĺűaĄąöÁ %µ<“ślŕ;t˝RUDm`WűPďĐâď^ůG}ÝřŁ[Z-ż[BäW~…%˛tĚ .2D#^q{Çč+&\?ďđ¤'oćhIćŹÔů{wSę‡ő…ť ×Ď˝«Ď‡Ě­Î›Đćkĺ:KvŚ·*ÁęE»ľŁ©v"»ÂäŽřɢh+eL(Á‹)Ď<Ě{N;Á«)O"»p¦Äń(ĐňsŞ,Ťźęb_îşAžm§…łŐ[î{•Í™ŕ¨hĎ|‚ÁST´×/ü ÔBn˝G-Ôö» ŮđÂŰ‹#ąŚq-Gý4ü_/•Řlr’Ęt$ź÷‡¶>O“xľ_žĹP7S˘DŇ$î KvÓŹ/j ÓČč\¸ĆňČ`C2?{Ş€ÁdD“¬Šópč§8Ç2€©L ő\býüęĺŮ{řŰZK4@A''†ÓÔ®/]iô §/e@ţü€!Ö h„’ ŮśBË×S÷÷ŹD¤Ç¨µ›|Ů€Púí„3ąĘŰŤ¦lŞŁ‡żńA®&đÝ’łý,Äßč/™‘‘ËYĎ/áÄŁí1t‡¶Î®ţ`†EŢŚé/ÎîóćE“ ]‚Vˇ;@rÄJG|ĺµóŤĘďVćď î6Äýµ ?/•5 TěőS'˙.ů_ůvvlP¤đ1?Z§đ<ő?Ą{ýüdĐŁÜŮ˝yÉB÷……Ö·N=5âǸ˛˙~{çŹě3 íoť‚oÄŹrfO~{/E‡uČć›Ë:Šą€óâZ43ÄŽŮćĺ›!dčŰ9ż¸S¤ŘĂ’Ůâĺ› 4rężY;:ň›†˙Nb]3Ćş„Ë»# !–Ś^o{EÝßQěÎoyBđ °GVşNč3v#ŹÂ)¨Ä›~BMtmŤŁf!sŤt řQČĎôŁhťŻ¤úÄýŕâ]Çsyě1;BÄş÷˘× ,Pĺ]čaüö0ýd˘]čQf>î ˙Pł7Đ[X/jşŤ¦÷}Ă"]č3®µzAşëłŞ‘Á†şQ§·5nę=h{®ČM»Ć€şRÇŕHm€Ő°Ď |bäXŔ]©łő“Ý&=ĺßÔ"h Ě&ćő`”OÁB5Ú5Ş?ťÉC'ŞͶ6îs”ç¤íÍÔ_@›D×Ö4g ĽŰ‘\YůZ$ŰŢ@şÔǡ¨p&40ť(@:×ý=ĘöYźÂJ&WŻÚýµŻhOý%Ŕx?e¦ë$† şÜĐwŕřýcb —f…ĹŤĎŞvxt©E[bŃ`TD‚mÓśjShž~ `Ń<(OaŕˇSk•lřđ 8ň|ľkŕ[(Ą¦±q¨M;+^EŁ·)Ź2ą%ŃkŮ óů‰‚bÇFĽk9ľbę`\VmúőAwąa7ŠŔy-Šú˛BYmF’W2Đ€S-]křŃÝägĂç˝Ţ?:ŐPŹ`#B%ÇëÝÎ=+ôÓMŘ;:TďęrG–ÓÖAÔ|’;Ś‹ëáżŘ`áŰ˝çUxU%9oiŰÁVĂ= â)Mo?ŞŻ·y‰§Ć»ŁŃÁ?ďr{@Ŕ”ř­ˇÉ?äěń[{żC"l"_LóíT˝°tPnKz#śîmčľîN8V)Ń×QÝŽ˛t`lKúV©bXó‘&$tť«®hŐĄĐ |Ö‰FË`¨ Ë-$łHV‚i™Kę>ZŚŃ0™ÓašłęĆŰňÎ˛Ž˘ á8-ąÍßŇ”řÇü.)@=2¸b)ŘđâŔĐ•TîäT§yÄŇqë=ÄtpčĘ©¦Ě ÉŠY¬˙آVăÍîK§ŕŇť©A‘`Tňâ@k…-žNß #©K ËÚ@Ö`Ěđr'3 )źŽ8†˛p¨çoJ®=\~ENgđjU¨4FÁ°éęŔŇL+÷ÉŁËď’7ł_ŁňÚGň.ŤEůĐ{ž ˇłÓ%úq2Š(é ÷.Ž#Ĺą%Ţpߢx_@§śk„Ő­ľŽű˘ą 9äűţP˝i·ëĽOçÝŠY ڍ ÄMIąĎŚD3ÁŘfŇş/š7—ňŞĆŠgýór‰ňۧŁ/oÚĎZŘć…˛a[` ń7%ÎłŚŇ±y«źż—ŁzŃʼn¤7Č)0ŃÉÉř0Ę/;¶}.ď—‚Ź€ëÂΑŹM;:|ŞÓńŹ0Ô`8ŻüŮâ…=0łR:x˘w‰J0ĄňÖ ę7űqsŕÉKč뮢+=´°ŁčRDʸ–?RţjÁiPăčÇŘáÍňĂ}ÇMŢ€u7‡Üś ČćíˇtéÚÁżú^ )2qç´|Íž„’Á¨ý‡¶ż lf‰™ű®r"ĂEů T2™ Ćr—F™‘űîBÂĚyyűÍřŹX8Ćő®6Ő„± §Ôťň7”¨4V\D˝âÝ_-!^­Ő÷˝Îđ±nČşW˝;Xăöżž•¶Iäý]drADgD)QžÇ4ö†ÝQŽX 6˝qk—)„v°P’ĘÍ~d05a“¶`Łě'<‚Ä>ŘŰ ÄµW˛mŤ:{Ýĺ¤. *µYž»ő”RM }Uţ±\¤ˇ÷ LPÜżý JP¬ Ĺ!Ţ)wěď5žEP©aÄű.ݶ92Nň“čť%ř1h» ű« Í_rŃ“ćďł ĎßR˘JĆăŢ‘˝UC.;¬G¤çŹQý¶űóÚÄúą _7O$%\a8ä§ 2(””Ç»í0bŤŚŢî©/W¸ĹoáF±ç5sZš”ź–ńđ7ÍőZ¤ŢŔMq'—¶G=KĎ-Ś”ď„@)?ŹFĺ®›ŹkPĺFélD¶÷c„if‰tś~öčÄkÄăßä1EČMÜ~Öj‹wGxDpÓ2’č8‰hľ`÷u˘ń ¶¸0€Ď’čÜ‹ôÔ9t Bţ {%´ )ü„XwvĘŽ»äßßËc×ߊě…ĐÂň^~2 Ăvy‚üи+Qy2 >µWĎřć¬,f™ńîëBË—źYÜ3„ˇĘ[ Ť×˝1ŕ..̎׿1 L­@{˙uz|ÝĐŰ]*Ć'ÜŰ}ˇGmK ´8˛ËźęNG{9Ň(ćéfżVľçýd@„«ł~36±Č ÉÝRG‹0mÜ îN“Ć űdĐžÓ“,Ľ%t]Ý8âéV»ŹňR5 ˛Ţrfi"¨Âe®K2˘éfż}':žz×x˝ëŹ:čÇM‡k!źqW˝ŹĚ—E—Ü?i'&ďV›'¬ď#dĐ©ě-O ü'—Ř«^§ÂR u%ÎęÚ@h‹4ÄŚŽµ‘|ŇBŚÁ­¶PxĹ3JÇĘR?c&NôÚ@4˛Ď+„8­]Á”‰±Ă­BXźV¬7’LÁ„‘ˇcyN1ä}\çülmRý®~R_ ŰÍľDXLh˛'˛XᎴíHŘţ±Żő+’ĘMćC[”›0¨]¦|R“ýÜ׾ěĘŁ˝ťżš‰ĆUćA7ă /^Ř_ íUtŔ+Řńmęî'ďń×ŮďW—\‰Ś]ߍÜ+úeěß }ËEň÷×6É­ĘŚB»/g× ×ýľęjĎň?rěM_mŞĐ»ďAlQ^Ĺü­ĹVŁ&OAşoyŮ"îXđşj‘†ďŐĆ6¶¬•îŰ!wcdő©,é$9¨ĂŹş˛A őâUÎ »aźgŁ5ëé¸Đ&`Ä2ÝYPţ:l:Ť‹ŐÂB«DĎŐĹ,%¤DP3âLJ Ö7Ř4"_BRŇf==d䥿—ÇĺŹ ݲć빆?Ż-5 ůąÔ§vŃ:Ü4T_iš±đŁ2]UŁ Ţ’Ş[EŰ×|<äÉ-hx°ŹT„žW§(±—ż­1Ô﮿žµü)áµlŇ„ ŇXzB–ž,¨Ě«@ŞśGj­>ÎX:pĘE5ýŁË3Ş)‰šűŽ”j<Ú$ „Y NčŹ4LTý^µ*3Ŕřu08aEŃGXMŃq:2%öŁOgŐE’Ştd3@”ŕ>¬;ŽfSĎ%ĽŚK÷&  #FÚ—}ČŰŠXúdŇŞ0ŚŠU(ŢpRůi#{ʇ™Ô™+ř¸D»1‡ÎŻy dw`:o!,; ·‡=Ć2š”¶ßKâöc݉9-+·Ű"®b=á§KžrżšŐ0ŔKtŻĘŞ%ć©ÚiŮ?^ń$ŽćŞ#Đ•8`""AÇĺÂäqĂňDÍÉ\ď©Ëť7!ÜłCEŚ[óÄY×c cĂÇčf‡ĚK†#-č_Ôçp3‡ÇíÓ¬[{‰-pCX±DŢşgidůxď ¦ÔŤŚBë˛`‚‚ěńÁ'ÇłafŰ?OH;#.ÖJ0}ÓҢ©')ňO¶śBaŚüVźŇ)mY‚2#ňäą´ ol€UÉ««Ď+ŘĐţµ¶CcO¤ľů¨Ăz[QˇR˘ěŻ&„ÄŤ‹„ńE¨ŕZ[R!űs2ő]iPŁ{ď§żOâ®Űá*»Ň Ĺu›C,Ó€í>LTä™)Q'˝›ĐÓ(P˝›đ[O‰·DĺŰÚ„xIŠB›(Ů^Ń7)ˇżMŠYÜlü„ś·Eeai{Č×ŔFň®Gî[VČ=ase!1’śb9Ô0”Ô~#0ďµ 2AË8÷şĂ—Â~䋱ďr/1÷_7/ť`‹D€Ň˙0Žö©Ő+0ŞĘýĂFĄĎĹéPŁÇBěËBćŇ#?ÎfIPŠĂłmwđűÁŤů˛70Únc›,†¤ë&ęËFn%K¦pÔu8ŰiČ€M–pu’_†°k%’n5¤éí´·ö—Ýöő„?ęÁ€€s‹ţ§ëĺ$Őv“8-ü×‡Śş˝ćÜŁäý™ť÷Ó•ëx_OŚŽÍţç‡ĆöáWĂNŐî燀í^’#a’D«­vUćz7_TOţ>ŁNÖ×U¬ŕ3Ö,‰ÖńËÓÁňîEŠóâNŰĺ–ŞŕłK˝şzÇőăUůŘ®0őŔÇ›†XFśł4NŐń§71Mú¬¸»ĺęĎP(ﯮ\ÁpäˇuÉß`M‚»ŞR>§vgŹWkP†‚ę!UÝWA4MčJ+°á•âý »[xT" f÷ÄÄCeMp¦ľ[ĐâşlůgvI÷Oq-zé„ŕúöő4ŕdpý+ĆĄýţ3[Ů”¸Đ čďęϵÓ$츯ońyq}›Ăúu׹zG‰q ĄiŘY^žőÔtXER[Îěô/MDŽ8“â]0âä ;vűkU5ńżŞŢ›¸…TCăl=gUę Źđ¶CV°C0±=˝ÖĐHń4i´_¬»Ç¦„)µ_.üV+5Ł“ĹUÜťŮwź]ä&Q¶^\J¨­™#&´Ň—v¨÷4§ŽÓ\WoŇmEÇęPóă\Ťv™¶6ˇ’(€n’mEA5€2$Ľ¸PÉĄAjN[L3l’ă‰kŽD,âőrŕŘş:Ź Ş ęK™yS~5- ›đ’ˇ#{ęFŹŽFťŐĚ|\ł¸ú±ÂłÄv„;ôd‰ZČV%ˇÁ°7Tôn÷»;/ď|şGSčţ€i(óO]Úy^Ţút‘†e|űä<·őmŻ3ž;Çżc"Ő´çdó·Ă+‚ĂZŰň9HJ“j–Ŕ,¦xÄÎÂFľ‹šČ˛dŃA_>ÄČ/Tl™ĄWTÎó ɇóśŮ ]µHű81ö”鋹o–efĘ“ŹË â<ómGc°Ţ¤Żj':—őm×MK`Ú1h~]»Ö‹¦Ëde-ďć‰h•/ăŽté-zhŢńď(H?¬uý¸W*ĹV¨ńý°v Łshzh&“`Äź,%ÔËAOâPyO •9:­Ńţ¸hŇ˙ő˸÷ó3Č‹i'$^ĺ'–ᲰX{´u˙Ł–2ëţróçeĺa®Öó“v(塜3ĎcU÷Ů|ĺMüîˇD{YßđUź˘frSďŁëN+Ťqd°ăÖĂO%ěcˇÄGo´¬›ń·•`§­‡Ňźs]вž"ĘËź6ü(Ž/~®ľl%ĂîĽÔňˇS>lJ€č¶  ÝOAq`Őłź6jącôđŮ™6?ŘYů[7*és@Jí.Úĺ˝Ç ÝŁ,}¶˘OŤˇn±†·˘Cű† ٱžmQWS¨§Ů–@ˇnĄOs› í®7’pŹ‘á`ş~ÇTvWN*ÉqJť˝řČăČ tý^(o‹9˛đkwŚ{…›Ď\J)®SóŇÄ8fĹ+čú‘{¶ł¦ťs×G’ô µ{-ć÷#ú UŮ2Š˙ŞŐą˙G2!3rcße™ ‘N†——«l#ÔĚYM_¤ĎQ䦖Ův¦+ĽŇx\]ŁF%’âĄĐŢý~{č %-z’Ϋ®éĹu´±·<”çžŢA;Ę‚Ô0ăpyYˇb«» ™˙§ĄGM™áňŤj1ŕµßőn·¨ĹÄ#&¬)ő–u™Š]WZ…Üő]ń¨jŐ !–kB QQ¸Ŕ°†·˙Yl2h˙7ăĄÍDd«ęÚš%‚,sďŁ>ĄŃ+dÂx‰"—@ŕą¶Ő\8xä务ďNŮőŞ4Ë“úx¤ŁďöE>äĄ˙&ľ_MBčĹ)[WĂú™ú>ľMÍaöę§oýěôOXŮĹ z6lŇS»5Ź— UşŢľČ.Mńö'§o{čĚO‰X׬Ďx¸â ŚŐXęůŁ#Ĺ/µě[h¶X׼ßÓΔ~"MJ-1űM`ˇ˘ÚPóNţŁz°n‰ođ±ď9·ĺßśPOx¸e€üóŔ3Ĺ? @Ż>,`ůaČÝ €ýXÂ@„$´fă mÝI±c@L@Rě~B”é±ţhŃ”ŔŢjP#ŇË—H¸zÄĂŤ.őÔh;ˇ„dÄ]ČË؉ C ˘ÔĐÖ“Ř &ĹÁ ěŹva @¸;äř#a— ›á‹%§Ŕ!0¤a*Iq(ůá+Ćü‚Pú‘MQ´bU :Pť=Ą|÷ŕśOa[źţŇŰ–Ő®żŞ5hĄ)¤Č·”g”Çź<?Îö€źŁwsG#!â¶ý^ł^ ÚNçj.ż‰~BĚGí:膖ěŮ`6ĄĄ•—ěٸ|ëŐńM˝5ČůőÄřtŠOÇnŹÜňkAKÓŽÁŹ2Đ€ ÷ŹßơB!,7sWŘü˘Ť%ęíđg(H΀ ÂkUAŚ'÷oÉ׫”‚ý Bč¨@sÂŞXMj ö%] Űh$‰QŰmy`FîěwMyšČbÁ]“<ÔZ É–«Łçd›¶píw‰»p‘%Ë<÷AďiĚxňČI¨H˙ĚÍ VĹ`ţ$†Ć/şIA1@•=«&? MŇUţÖuVMˇÎÝŽjčm2ÝňŠA˛ŘľąĎˇâ ¦«&AgtűŹd˘,FłµŤô"Ůé†Ř~HL:ę! Š^oLTÇ‹D˝a^Ĺ‘Ú ńĂXd‡°ĺa}IÓáLPQ#ÓÎD˝)-·ý^c©€Î;-ŘxČ⾊Íy|Ŕ7U®yŚ »†·~‹b0@n˝FIŘ›ö‘ýF.0HňWßFYŘ_Gµ|ʖݱŕźUpĄŚĚ{0ˇ úŇnÜ5nÎÄÜž«öľŇp="ŁŚ®MÍNűm˛Bä!60jáŘF䑆Őx*({ŘůJ‰M]h ?$ŞÓž>ÂJĺE|¬ÉđÍßBÄřwěË7ă˙ )¦ź-ÄeŠ;VD‘ÚŁ?ôźz¨Y…&°GĽę;Ôě‰%j­ŔŚcüLoŘV›„-lî7ŕňř č«¶ ±żĐ¸ĎWýu˛¬¬ĺHB°qkXËâ"_`˛®*˘K5čîŘM¬¦~Â!UI—Ţúd VHSí#…cq‡«˙–ý ¨÷4A W˙u¨ä€Ú—ˇ PGíŻí¦ (&#ou#1‰IUICPÍcoe-Ob >Ľ¬$mî§ŇQ€OĘćvZç" ÉŹĂí¨Ţ^ÍŹOÉć¶[÷Č©‡¸ß'¤ĚT«Té—a¤¦2ŞĽR“gĽOÜ_&1 4¢ü ¸’ ů–uÄüLúWĘô˘&?ĂŞ‡ć·˘†‡ i(éÎň¸@(n&uĎŠ¸#‚bŚ'ä[}ćÝ. €h§­Đ Vʞ8˘?ln>µ.@ü2ˇÜ*#ĄS´ĽĐ°#.Ķ×R}ĐA·`˛Ä‹Y–ôŚ 7ÜEp@0&ťBń1¦g™śôŰŮ`ń©kŻgmĹ‚ŽüS°`DŚl[KˇÎ9š×H˝‹e`“K¦’Ę{¤„nż'ÎĘěĺ›Ňa/ËŞŢB mß-ídť˝Y`řŐŚěDť˝i¨EÉŤěřś˝µ Lź íŘś˝‰˘ôF íčś˝ů¬ěČś˝©`ř‘­ěđś˝5 LŃ­ěĐś˝É˘jöô=KÎÚőA€őąJÎşä Sm,Ě.Ěgą'Đ,2&l±­ÜM÷±×_b™ĆyšokŔźë„ݍ€Ţ 7›±;TŹh+.ŕVöň¬FO"Tt[Ú%şäSŮ»šĂ^ÎÚÜ»dmǬ5`uR2ěă/‹d ŇföDŻF˙ ¶kKC~‚a¦-¬ŔďĹíš8ÁËBz»ą‹ĐhDş("Á—¤Ůmť‹ˇ’ÜŐ0‰źpĽňÄĎ‚!žÍhŃ2~Fęňý +5ůF@‡}ĺďh3ęO/ Ź›[Ä‚â0żĽ·±*ŰŻđkÚT@t¨v5üW@ú‰g´wbµCq”ô\lFCq É^>ŮśSěC üřź3ż8:‹r˙ŰăŽ)P~úQž˝h"ŹóZÁI(łŔúiäß;ÜÜS-âŚ`ÍÉl´}şř›·Cް"Cr6´¶›ó~ţgÖ.fôŚRžîőŠŕ€`Ékçłzőŕl©lí{9=}›ży0+LŰvtîM˛Á !¤ÚWŞĎ”ťŔ˙´s hçJŃ®7obĎ⢱†:Łäíń‡N ‹é‹NRh̰)JôEô~ěOxĘ×7:˛9ły§Đ•T]Z ÷”$ݵý¶ÔçČ×řF¤ŠjßEi®€‚뙜88nÓąNËe'ÍW©1° Ä1±y8°Ëž¤´ĹyhG%ŕÍWsJ#ĽĺĄŮ[‘`˛f#h$lŔşűB¦ŕţŐ%jÇ®•­eُoÔoĚ»÷Âç ?Żpűô1 Š ¤9ôhNáńĹEü6̰©żmă{Ť6®NtĂ1Żü¸(9|moý+Hók“0îă Ň® ¸h7öbYú\'.條$Ţ^­Ů ĎUé×Tó‚µ§ĆÄô?ëŇë´đĚ›9T˝• ~ČŞoak–€}• lÍK‚C—ř6¶ 0 HëĽë›0Ć$-®-ť(q„v{vž\äF^ż¶«veř­ŔU!m0l®Ŕ\Ś?ú.nőö±‹I[蚉|ä™5ćŇ严[3iůr°eíEmЇyîRŘ _Ü˙ :A.oÜg¶]—W!FýśL˙9Ż@M;®@Ś ăľLMÜ{„§9!FD6kSę{ěŮ °ÇíźräZ»p«“FߡJş’Y*W:«b“u!–ÓRÝČ„‡Ě8˝S´8y‘ěÇVá‚ ĸµŞő zé@f~r#'©Ę5Ě7ĉÁÓŃÓěH^ MPĂ“¦Š Ű™G‹vĐk9:]MËĹ%@0*qÝzeę¦bÝbĹąŹäú4Ď%+(()ŢS€ÂÄÖ¦÷ü<4(©.®O!b4Ľ?ç™3Č©ŘT€ŔÔç˛ý;¸ˇ.26PćhťzĄŐ.¸·ô7 ×wlÜŔŰ$žo*Ťň»^ş”ěB­ăŻ\|{čë×ĘÂŢî šeż#§ł~­ÚK„‘z G pŕW«f-WA0@óŽĘ˘źiĎq7¶É…x&¤K{};ł±cŠľë‚°· ĂyhPĆuKwM;.ňę\646 Ďľ˝ÉľKą1ČŘ5¨vM@ 2îKŠsđŕÇ aŘŕŤLżĺ´ë–ß5íXk?p™]Ć?Ó4’^[ŕP¤‡ëµË(ŚYĐ+ş»“ů@ę¶{R&Ý,|]G»Ňŕv™Í+)Xi·Zß´0PNŐ‹_jr_BᯇłŽFGóó»I;íÚŰ~?{[nZčĄ#ÖÝÖńPˇ¸q!Äo”r†özkäönäÄN!‹׻ݸ+@ŤA@(ą»P€ŇAqcC‚ >‡öjí†GćnŢöÂăÉ5Ś© öJízoĎ×ÜąqĆŢ3÷áFöŇj0 Ň-Y‰Áŕ[ŔoÝh© aYńLKĄ7ŕÂăfŤ\ű’Ą i‚Şůˇ|}:Đ“ŇD´ Ř*p ŇolĢ® G;㆟ ’&'ľ{!‡óâV¬ŰgA)çŐWý{ő0ósŃťU‚ÂÚ”ţoI–ŐAg!IC:°’ĄR–ŐÁX`•­ M–6!…Ôé‹ëPá?âfÓ×ô)~™OdƵ (`:˛—¶˛Ü>Ű‹EřÇąŽŕńAJR‘< îÂ4ěů¨I0Đ8ŁăZăćf…,kŠ;ŘÜÔ×´|O#Ł I4sÉ«3{ŢĺQĂţT·‰ăö©ú9"7„“@ƶ€?]+Ë™ŃĐ"z[  ´ńV ~ wÖ‘őQŔ… AĚů!^ŤDAÄΦ•u_ Čźs”}_î  ÇçÎXňsÄlR:ŤLČá:™¬˙Tbšă:•Ś(#A ikőc'ÜVćú§ť¸Í:÷ͨŢĚßÄ®˛Yës˛Ął…ëĽîş|Ĺcc‰dYúŁíţ ű`c‰ 9č€Ć \䲋 ?“ÄmrąŐáĹ1H[CoŤČMĄŇöIÇ'âĐŚÚÉe› ~ţ·Éç^ÇP!mŤ˝5"#oŢ#fSČ:;€ˇN\§[ůgÎP1J€Âé/á0lTMAę°Î´rvĎ]‹í*ý袦źÚŮUŁ–:™™ŘéŚl fb‹OŤ ćřdd+÷ý´uYÁ‚Šě¬šj/ňě÷Hމődđlî#żgŚŞa+‹‹H}cđ4ŚŔMÝŹůłůSŤz?ëĎfÁ÷X¤Ş“Bxe|=eĺ7\$â C±ą­Á˙TRQçJ ö€žČÜfĆw”¤ŰňR›: Wfw®ésôq1]lȇ©sK7#$ő2Îmćý %'ńÎ?oëü«íXÉLC†¤s›<ż°QčăÍŻľ¶ä±îRžđ߯ţ5>Ą„äćŇG\ †čî „z7 _ayľćϲoAľŇ3śaćëŃ5č3¸w)ʬ(É2{6ÁaO©ť Í•Š™Că ­ölÖ¶lÔ!'­ŮMOěÔCqŁ8ŤťdP Ěç:j151ížoߣ†µ\±€TOLT¦ë˝ĹŐM¦ďI¶ĄÇż/ކ{ńę¸ ônÇjZyN- `ܶŚ6ů*€xwl册,E7}ČŔ!Üßű¤`îf‚Ą†6lá‚ to•‡ŚůMát|đ~/ŁëwëÎE˛Oć`ň=Ý<†2Cđo‹Ľő§{L‹Ű~ÄŰ艸]]Âż}Š`.mš#Cď:×ňťS‰hŠ7É2z›é ŃxOťŐĽ=™X¦UĄí7\á¬xÍşâ=© 9„D®ű®ëB‡cĐ9€;řňP7‹\ \Ú¸ ŚhŹ|LÔd=<5 : E­‡ŻoÁE^äÄ{Ćżqřý`q`sđeŃ[NÝýÜňoQ)ŹgX…u‡¶Ç·“˛‡ľÎˇ‚qFv„÷đJu¸NÝ٨V€ vž.*†SÄą§:čQ¦ä߯YčÍů^Śr¦/r‰CnÉ·ĂčíeoVbżç±r¤/K‚öó 7ăM=dÝé ‚ŠűÁËVšě×{­'ˇxů _¤ I­¸ÎČť*”oŞ !Š î(™c~¤F=žco[Ž `¶Î%@•«Ž¦¦ ŕdžKM H€Ş«ĐTʱú^ĺŻÇ4;čiR¶“µŰëŤh±?*hĂ­ÔÄ 2z#¶‚ cLâ÷+‡j̀ߡ⯜s5Ŧé>B fŔ~E[J@jŕŇÝ…‚Ä9'ÍżPć®J*¦oŰ)Jz7†‡)BômřqąĺÂrVž”Â`_»řA˘}rDëń(‡ůĚŠ)—›©áł(Ań͉H€b ż7źĺÓzůńüŢ}<čuÄQżÔĂÝś7°‡ărÂÇjŤu_.ůărÂż™ć™ÚÉY!E¸^Ä}>g h‹÷hů !Ę…=ďo$¸ăs°‡ ڰ~a§¦‰ő HëĘžʼn^U”Jçrś­>Mq)Ť»™±XČÖĘăîĆç “žÉ•ú)?ĺRÔ"îŢRĆŹ-eź}k' Čś¶«r°ÓŐłÍ4¸1Ö ´c•ú›hů‰ľ"(‡ÂFצFPq˝•{]ž˙śžypbĽ_4Čżdtî”YÓĘRfKü'"Ő¬4ČĄă|©óń2Ä7Yü+*/‡~ ÇůLŽM ¬-G‚·Qʱ†çT9đ6dö(fôů±„.jśő ‚ʤ×EôC_j–rüŤ„)Ě)‡ą#ěA46X>8ăÁWŔ_´żÍÁWĚ ¬ń¨lőĚźD´ą/y•hÇo4îŮŚ†ň°b´<%X:dPÔ*nˇ€Đ3Ľ:‘S5°nqŻů°nŃ*©#]Nh-]r±ş'^# Z$˝äWGşŻŃŘĎ3ń2ó"TuD –|Ď$Î$ű‡™Š&¬óüâC§lh˘ŇĂ…)ŇuŁ o'®(±ý |Š ¨ýTo'˛(‹ńQQ1ArüsÖ°$ |LÇe1z +<š“±ťąD9W–A Ŕ§gĐüĆčCÓyo'ľ(Ë ‚& g<ąvŔ–ń’·c}ô„f°H$ ‚P\ |p° Ńq}{RŐ_ąŐ çĽňzr iő¨[#Ęô=F!˘–§ń˝~$/E/ůÎ$¤ş”č™xÖ%ă;Ŕ‰LżőĂľ&NXýă4ÇBeXS–±L¸`Ć”í[ ă|ň+(~×›€/‡e¬(#ô‰5c­XÉŕFíĽÎŞxâ’?ć“A ©}ŐAx šő: ´|ÉŞ˘B9>:§ŞôĆq‚c¤Kżbźâ÷Äńʲ¤&ŔĽDmÇf™3Ä˙ĺŚăÉ5ÄAw8ţykn‰"âyiÁ掲ŞůŁ5ŠEP߀1™Ż«´TsBěłEhi¦§Go83 -j/¶Ýi° ¤’FŕŹűĕ Ůâ×^,ŠĽř™@ĂLĚMDdŕžÉŰjôb†]fyŕ/I‰Yćt«.öSyx”yôU8®śűŚ =wIďţŤ:LLč<ú™î`ž˝Đ»dŘžţB‡‰ZA\4ęp‰]Etťů‹&> ĚpÄöŔ)yąŞ9‰Üą›KŠÚ˝×Ě{-Öo7UPR]_îK4˝ŰŤŰá>‹@¤ş$ľÜqöz·8·ĂŠ>ޤŽ\1ĹŔ‚ _¤?¸bLß>H—T†ë1„|« ř˛î$/̶ö%}cŽ>¸¦ŹŢ|bPěµré(†S>±·äC*IJClÝ8E'Đ9\çä=˘VČ2ă˙ÁĐ]ßăÉ´őűŠ(2eÜż¨Đ2rż‹wë&AŔ»2َ¬+ô&Ćó—^äŤÝ}$RtS"Fą1^ ˇ@ĘšĺÝŢę}±Âč%ş9ř2â™!ŤöŻ?F"!ôr’ąľç±†m§AĆ ź óĺĚX#hÝQT؎ ťşÜpGňµ^Ą4KĚąÁřŢöĐ ^Ý|îçę*Ĺ”¸őůÔa'9ô ăćfWĽ\ëŕĆf[±ď0§z·ž¤¤ĽE‚o“­ůpH¨^1Ż(rżň®4š&ÚP@'o’ĹZ‡»ž ś÷’é—ÂôbC»_{ד ×p=ˇc\%f` 2‚^+˝ď-Ą&ůb˘6G&…¸Ĺ Jmń@EOď„?ŹÖ~Ń8—ť [_ ă“™˛é5ń´DĄFWqp¨ ±c^Żżß›´Pbąů¬ćĆIŔßtĽ70Q‹ĚÔL§ô±Î qăń]¨„Éłz|5,ˇ6ď×ÔZÝ&˝Ícüt@덆}v‰žóω“”í–RP ĄĺĂ‚J Á»^~sZ#޶š%ää[ěĺOŢ,ž,ů-ÍĆ2Ú@·ę5:t’Js;±HárŰ";4Ę9—uήZĽÜžčĎŁ\ĽÜ¬Č›ËŁxwţá´J·÷sÂđi#;ꎙn;cňUř‘93OůĐKĐ4Yf†ňî›Qü.sąx?f!ţýO–őőžĹÝt¤)DŮd˝™QeÉc€WëOőŮ‘‰Îz8x~+Ĺ… ¶ Ą…UŁVÝ1Ň•üÓBľŽ™…ěÍWç:_Ő‘<ź‡áfß:¸×Ź ¦ş ŞÂĹ2ŻEłJ ±KąňÇŘîJŹ·č-•ŹłęÍwĹŰ-ţÜšeuU«Üç\53ÖßJ…c|_×olľIPŠŮÎŕ’ÎĽçČëé”ĆňÚóÝ7ÖĹ »dj‚ډTj‰ š‚ąŁéÜ$ďŻŃ_ÇI«!ô·&3 ¶–´;j¤4ű|_7™e[žŻÔ\gĐÉű«pťHÔ ;4 NÜgđÉ bű’Ý÷¶Wď7lÓţ@ä¬JbČĆk÷g|śÉ~HC‰ő˝q ‘öÂ]ü űň^ýe§®:Ú0l¦BúĺęMdĹsâoćş ĂHVě%IŚL'‹NiĚ/6Ś eź×C\rÁB™ŃA Ţ;.Ŕ‡/őšJM<Ú5ą¶_'•Őú›|X°}<"Ł,ŃävBVRę]Ş’7–łµĂé/N‹ËŇ–ŘfőčĹńWM®¶šűo)„Çs?r +Ś+ĽhËT>˛+ü·qĆťµ…_/ Ň7TŞS”_ňô6)·š¸‡Ň±©>ž|<şO›mU>]U ČŁ?9 ±A±×há¸;W§öÓYçşLÉ*M>čÔ©9 FÓrä+Ź3cIǬVűÜ UăđpnWN‘#ßqHrd2gL•«o(/Ëş 'KĘ0¨Tç®}=¨Úvě!‹ĘfDR_JH:¶/„Z' }˝ĘúVĽNÍHNО“lX?hź~±ťń˝ĆĘ;n>Ę äĹ7gR.°´Q»I~=üţz-o¦?wT|9— B*‘ ‰,ËSĄýIŽR!ÉŮýµg›+䪥tjDÓĎíą6ÇÎy” 3lľ¶vĐ4#tuşvhQďťG±^ÎŰ~$?D˝žĎŰŽ—@dcľö°‡^+IÄV<…2Ą‹ŮĘâýH˘37ŇyRPm/EôT8Ą"č0Ŕ˝žÂ« ‰1Ĺě řjERc.r)–] e‰ŮŠĺÝ„Ŕ†•&Rś8Ň$Í2tI‘`)hßé´Ş’źÁKäëÂŚßľ'–ŞÍÖ9 {R?ڵĽžČë™'Âń1ň=ÁĂ2v)ő-ď˘\€•¨•ÄŰ!«O±0€Iđc ďÂ>Ľ}Î447­d”\‰D§ů)-©LgJÍpv­Ű«ţ´;%í§Iˇo>çËłŹ„XÇfF•…ôŰQĐÖ×ieŮV—rÖŹ–aďGę<–ࢠ®É6Ř׫ űd¬mSŃ\0–ßQüF&zÇ˝Ń×ő{×ÖJÚć–“)FłÉ{äÍS2‚sĘŁi<é«•(Ó’Ýď{tŁź÷˘ĽĄŢ…Ć<éĺ¤I#ő9 ú<ĽMż/Ę6k˛Ţ—nS1V|q É~ýzPĂůŘ!dgmÂĘĄ‰ćëôwG7ăşĂokŚú´ĆŘřrĽL_Éüş nÁppt7UŰaxßé$(âÔÚé࢞RŔŰ绂OD\?ę„bלzë»UĹëžGnl‚É„9‘´Úz‰“|íMĽŰˇŕŹ^ăĂ“U öő‘śéŁŢŻyx)É%<\|{IŚÚm}óá­*Á3ŔtüVVÉUj†3Ŕ|©¤¶Ć`ÍU‰Pď„éJJ-Ë/µ6I9 4ĚîČ‹–Ü,(µ6EŐVŔaí«IbúÂü‰#|ÔÇaÝý÷¶ľ 0-€ ŇÝȨk§ĚÚÁZR7śÝh¨şţ©ÉÝx¨/ďűg/5ěđ °_ôÔ“üRý|P‘+ÎďUg/u‚1]¦Ś+/‚p 4ݨßÚ}h¨C’\tUaĚ*2®öůžD€ý+×EşŇżĽdašpŞúd8ĺ„$ôŘp,üjŠłĘ˛:F†<\VëщsÎ{ňÍĚDâÎ˙ě}î‹°({pΗ‚BxźJÁĆ/—eÉ8W CM˝¨V”ÖĘ%¤‰2ĘęĽĘÚjXć”}r‘®0‘Ó»\÷ĆŽÚÇ„úhŇë”?4¶šQÍZÓ.˝˘Ô¬Um§Đ'ZÎŔJ;ž }µ@¶'’Ď íNýa˙ŽŻ”y*Łč2) ®ć-·5 %@h ť ÖÓ üĹťĘăČi­ßń:÷`ĺăÄs2VĎŮ›uD‚¸Ô:÷Áýż+Ú/ż ±bßwĂŁY2ţ|ޏŢęŢŞěňf@€ŠH Ý‹5fń`@˙Év9É&†÷X˘ô0ŢÜ‚3îą„&D9ˇ2Â;±Őurz“~ěZD›„T˝_a,<†wc‰€¶ĎءáÍ€ü3nPsíűSUÜ@JŁĺĆëŃ€*!&´­^žÄ’űWgjlV"Ľ9ťŤ(FĎÍźÇ]ć„ŘÜ#¨±śĐV´Č1ÔÜcŠŰ4É›°§]#IБȴ'B˙’Śâ>´'˘5Hüđ{|¤ÂŃ‚Đř)ŕď9T~|č´ńxţĹ DS¨đžřŁY´ňocFăMh Âőčx;ÔbĎť~˘qżt-‡‰Ôą$82Ő­!žÝ´=Ş0''d¬ÉËesßtą!5ß,żĐA…ĺÄoŁ˝ÉóĹrCuł‡ŐEşQűŘŁ‡˙ěľ zß5'FâŻŰSGŽsž;Ž®Uňy@»ĹăşqbJ±•Ŕşťlmrš!ď›O÷×řľä˙–íď‰.Ď…ň‰H ‰ Óâ;lÚędž˙H?H†mľ,S‘ÓD®•r‘riî»`Wü<îŔ-+Ćĺ,Gä”Cë€Él„Č”›ĂĚ©ĂÍťE*ň› ’yôńâ„<¶9n94Ą$Ł2µáÍDvj"r$¦Ď­ű‘;‹ZV„ČŮ.Źj GIrĚK”t«břÎyźE†Č¬×MrĚă?Ů˝YŹ—«Ą"tÄvÚl‡7í —˙ŚŞ’÷ĹŤ —.ÔÉîš{oQBČ9-ŻdË®M.€Ć#'ňëXL^"—,>Sä¸#aÄéXČTţ!č»;ЇG>ĺôšµĚĎCă‰őȉӎç~0\š€(<ĚA#(<®Š!ĎĚĘ()g’ Á™÷˙áě,|ĽQÖŢÖF”M:˝đě7«Í#ĎŮĘĎCń¸šŞ:śY#<‰‹©:ň¤†ŹŮŹčů:ĘŹĺ Ę/–ł 5î®¶ĹÁR Äż tŰ9 ĺ*] Šč˝ÝRPÖî Ę=¸#´ÇşÝ† `1‰ ŕŮl 4çIA;*2€%『jKy@=¶ľ™¶dä[VT€ęÓ¶ňÓ"|©„ť, }i~ |¬K*Pî`qVnąGć2|,\Jd~Fnžŕ ±Y}uch,ŢŠň•?& ĺPĺÔAjŠÖ·čĂŞżý uÔ*©ŘŰÉ «"Đ~"˛?8úîáťěčľ,Ä…pÓ¦-FćŠ93„:0ďÓPŹn«ByfÓg2›-F¦ +˝µărČVc^Ô…|˛Nă&·5…˛JM· F ć Ü‘g×nŹ‚ˇh}óó×ëF§ŹôdoÄ~ľŐŠ˘p6a¨d=:Ą1ĆĄ>ĘŁžĽB8ĚűśźĚâÜ©_ gŮĐLëóŐ-~OÍe {z<€&ăaS_śĚMĚŔťŮÓ| gůXÇĂ]Äż=NůáßâšŰń=Âś¶Ď21îúścĚňĹŹ›î},çC±ŻMĽqo›M…ŃR;oŕ¤9đ=®Gŕ׿o›L™a_áxѤq NŁIGĨöx©keN˙ÉŞ-LvĘŚŕ äBY «úe)ľz’ZL˝'ľ‰á˝|ń*D3Ę…ĹŇŘÖQBxä$劒ĄzK–"kŞŤ?Q u‹9—Eq¶Č™ľüc«“0Ĺ _ă± Ę(,ĂÄ+'é7PÎ nŔą?gX„†ąz0˝#PŽşJ@üjůĄlM‘„OwP@ ĺ ,7©h¸4 €˛ťÝ˛‚ Ŕp°µ1Ń D 8×ßż  VŐÁ‡z8ěÖbÜî„#š@Ö[0çe-«bB íň«•áăÁ)ڇKČčô& LÍC0±+Ô'Ç! cBP+Čjd””ŽE)‚D0M2#TšÇ_â t9ÓĚz{%8 A;iŠ—Q+ÄĎŃXso»Łß:iÂ? OJ©ž®ÍŔ%Ętô6vm„±¶tM€ń'ť®„™b*n%’§î©H™ź®E${FAĘxż!Ú‡Y“r=ŕü¬3¦bĘ~…k¬Ťäjg-»*Ĺ=™.Ś×ň.ĺÄ]«Â=‰żTŚ/>…»‹Ş ýQóŚ4`Ŕé[p*ҶLM÷ۢL"5mvĆ˙$†S+S°<>|Ń0–Óą‘¸$q Ą<ęçe%ý®|JŇ/ j÷ym›z=//Â68U@žÓčR˘ÄÉtcśĺ§ş:¤n°sŻůęrńĚ‚4 SF‘_ńŇĘŁ ™ýx°%T‘ 9ÎĚß_o…Çv0ş˘B§mJPčŁ1!T%ýPQf˘DµÎ-Pčł×Q%śďŃ3°şÓŞ»sz¸+3jR4.˘‘@Ú&ÜuP🌪>śŮÔŤ¬&–ĆhňפDT ¦óź¤N,KW,ďZ©Sç’Řô”I q¤Ë-٦óé&”tňű•+p&T4ňËE»Ó#K帜Ę_F0•=îËÖXPçŇ’BµîËąâ*«CŰËÚżu€ń+ťčGÎÄ)ÓśśZÁŔHĹrµ•WIćö0sx±ýTM\Z (ţ˝ˇŞŠ 87Ž{,a«Âvů §í/áďóPŔ‰mČčyiĆѧęźŐ€¤*şJWÔ‘ůJÉ'{»7cᄣú:©!…s@%îülŢź‘U‡¨Č;˘ęcç1fb„â1–\•Ő0Ö™jëÚPY%Z)xµ^:|a°Ĺ?čÔ‘ż1ň:_&5ÍĺQQŢÖoţ6Ó>^Łm1s™S ôu^°ĐůŮt\sŃŔxÔéD[’ĺm»(w+şb޸×höű %—.Eɸ–¨‚ßÎť üöÄoáfwtÍĐr~_wdEp-?ŕf˙ű¬‘Uć˛űÂ_©‰!{ŰĽĆ FťĘŻK…Ţ´~›M:ęđˇ 'ž«,ĘňŔŻY&Â… 9WÖĘrˇ’Ŕśë.V8U@ťVdEpÝSÓă.DT7ćô†»dŢ{ŘýůĂl\0źc*50żËBřŁS–ôŰ]ÓBE§×­€ůŇbľN/-|ýöÁrZŘ$ż6v%ŤŃ‡mÔ۶]¤ ôʍĆFµZ+ŽcĐ漕ď‰P’p´Mž:ć°TÄr–ů&Ů“ÁäsçŃäÓčÇČă ňH 83ó‘ÍKĽü·:Ë8ĂX‹›ź›ężé˘ąŐc*d‹%7+µŞLLMv-‰eUčëË%9«´«L|›ăłĎRJ7uNÚě[YŮíA\ɲŇ)v–ŹŰűś9°ĽĄŽęűľ16ľÝs@ź4ř KńŽŮŃ.GW°W Q%Ł,!s|‘ÉRVL)ç]*keÇbt ŰŐéęúh{Ü ˛plú'öCQ9/÷ËRUMIëP°»«čgň*{\Ë˙9é ĆőÄv§ü®ˇYßőăĘ÷őŔÜÄÖcOťä¨ř,sŘ”"ŐÇEŢÝ%dĘňŘjŠjbúúôÖĄ˙db|áüŢ–'ĂE«ČD­ËuŐ˝5Ý›tG9|®j\éŠ@ÇmŐűű\*{ÖsYß3§}x˛žtŞő)Îö«®i«ŢŻ×ć>S=|}{YdÁ áŹ>0Ż'ćz[´lű·Ó‹…f0t@ÝÍ×[dA7Ý1˝óöýBł†2I^˛q|ľÇAűsŰŃs-‰DŃtw‚Ăć÷'ń2)›ěö­é8ş‡Ę±sŁăóç§łŕĹ´ŞőÝkÇas˙¸»ÓţĐÁÚÎ+ŤMXĆôŻ»%é2Úµ&tLnyGOŢŤ-žNć„l‡ą®ř÷Ťç¸Îá,fTŃ(+'Ś!q\jëVµh<. _Äž:5Őń*oŞqtŞšwÂaąyÜű+·iÜo€˙Z‡â? síp”˾ο…®8Ł6|ťQ޵ż ÂőĘ/6™la•ÝĄWśÁůÝôgluX›}Ř,¦Žp>Űt˙`ó˘ĘťŚ&”Ů´ňžeSśZ(Šü8Öź€¬Ľg`rűmńĹ”çŢ‹ĘęÚ>Ęj_oHŤ‡:ů¤bv‹Žá8Ň5;OHÎMi/Ną4K&3Í9Cę[$epČEí  OäČŹ š“`”rö47OV5łď!˝ř^¬JćŢ>ĆQ¦ý)e+µp!»őń’ĘçÇÔĽíÎĄŔE˝5^¬Ą§Â×›Ó+©é¸Eó㯕㿅L÷PJŘ.rëĂ%,o0ÂĐÂ&I©*§ŞĄ˛3Ű”&š‚ÝbŹ­^P¸ÚîÓ92Íçy$»ÓŞĹF/4=&/Ć,o]ĽŹdś_Nx S*6NH^§vI¨šÝŃŽÎł†x{JĘŮMź ·eşoi'W%$žś!m÷3x* p» [_Kv13}đeS¨Ş¦göÖëě˝?sŮ\&U¨ŞXsdŕŐçVyŤř<†=*,<†DŚYžĄŞ—+*h»)y Î9sجtóDG'INaĐ™F/IĘŃz4yMcŚÄކöY˛ëÉě݆“;N›Tĺ_x=Żű¦V|„Y^XNbÁ-™˙\ňť„ŁŞž•/ĺ­ěňC9N˘¬fĎ‘Íćžťá×ĺ<°ď±÷8v~¦l殬aí¦ý¸=g|ŕ4É5ĚŇ-Ňý•ĺnź¦ĚškÉŰ-uđ‰ä–x¬UŹ;ŢXżtÄDe‹”骔ł»üË5|’‹ŞrĐ{“’󪬷›ńýľăMeíqG9˙.ë/f}š“ŤTDÎS}ú“ŕ” o¨ĽŽĽjîŇźpkđĘ:Ĺş8YŽĽ^Ł«™:w±Zušż›]&YÎ(#ť3ďn ”2>¤3ďţę Ow–!«˘‘­›¶ţP™­¸öą”˛ĺ™A)G^ÇRŹŹ~(ۧŚ)ť˛W~D'°‹Qkęş™‰O\2<4şŇË{,‘˝»ö”ůG>Öꩌ­9XNE˝ä´»·Z(Ľ›Ű?/c×đt3|˙ŕ2ú~ŚÔ¸ş1ÖÎ|4žR2?CŻJ+oÚ^psú7Ž{Ěő¤ůz‚/Ä7«Usv5’{ľS÷V(eĄâŻ7Bż*%ÎŐB'›ĽK#p´dޏRĘŘô0=Î. ËžĐz%ŮŹb źd3/w7~ß‘ŐB=ŢNN|_ŻíR6(N3ßG—8Ng°tnjŰÓ‡Y§Č Eo·°—,¬RČŘ=ŃE 2ł˝ŻŢű‡U‡ EĘĆĩl¸i­P´&čĆ·Jo•ź«:­ŤÎnCN'Í’ëA´Ę龜-‡űđ»9D˝űÁBYÓů`˙3´Ţjy40¶––sČ6ęç‘Aă)§yăŢ@ÚĎQE\çx»şHßŰuQmb웼/2Ô­^](Ż;m‡PÔ\…íj´Ż÷ř"nö»A>ű  ř*űEi{tZZßé 8ĹłA>ďÇ ”ú‹o»ö×S<íĺf!ÜlrJ"íŐf1JĎŤŐ®ŕi{ş´ÎĄż6ňp]©d&j‰ä®«wé°ł8ö°ÉgÚPgěA“Ż4&°o˘˙¶^×€X´ćeÜ*9tlűŮů­ÎôGú,»ÇFëą}psŚ6t wď4ňPĽĺMB(fz» ͇ăv:#‡ĹĆÇQ ďĽń­ĎÚ`§$µDŕĎďćČ·µäíçig}ěVÚNšÚ(Ă%c6¨ycűű¶óE˙6ęÝŻ}“€6±˝ŻýÇż˝ľeŠ&z(űĽŐ‡ĺžöđg¸5¤´;öčgx&Ôw?NŰížK“żrđÄÄĽe;J€ćlřáĐZ‡Űő;D>﵇ŽOďÉ@»Jđ!|xĘřď!{ÍG†µXí“f}í6SĂ{ČëąAhëY˛¤Ď^ŁîJ:vžĽîÖíhníç/RVUŃÝ’#+ăĘä?ł"§ŽÉ]©ÍąńŞ.5ęž]@Ů7Ä1·Â—ěÚ⬨«/úBE;ÎĽÄíˇ°®(>mö*Źž(Wň\_ŁU›ł¶Şů‡/ŘuÖ=>s­IhÜ=Ď^ú%Ńôž ®ěx–*bn'8ľô ŻlytcüąŔĂ;:Ľ }}ż_ËS|*řŚľ4„ÉŽŤ…f©f)'đÖČ«kÇĽýFˇŕ Ý «Ň6 Şä_ZYü mŁyĚË…™ŞČĄ]‚}ÍuÚýŕU]LvP1ţ&?őd A‘0Ž’©]ZNŞ=T=ä ˛g\rôoŔKń”‘;Ž^8 =´’¬ůDśBł6c&Ü7ˇ#)ÜW$4šĐź›kŽ6*’ž¸ďŕµ =ĐŮž˙F˘ú˝Röýn”đéŔ#dŐÜŔ+ż1$‹f%2«fynŚ”{Z˛|VÎ[2¨WŢBPí¸Ő9«bP pFĽÖëFMX˘”·0l+MbOH!﩯L’F•aČĽkhÜl ĽxŮčąz˙4ćÔýäńß"ŃđÚř3ŢW÷şŻc<µ#a=a˘âc8,®ök¸šř¶h†đę’x9üÇA“f*ŕ›}BçŮűM†Á'_ÝŐőÎď‡-Őşş{|í±’ÄDx”şóĆ´9qYÎH•ú;“q÷Źr"ŁŻÖεNľ5#őŹş…‘f 8]ć"Đ:SŰţ8?µFms9€•×±“ňmŞ[™ Ż^) ĺ9u;LK>ô ›ë T3 –ý3Lă‰h€M`C,Mé:1±$Ćôt)–Q‡n-)őźébF‰äĆÔÝ)[#*‹Ó„Ů&L·¸ ­„µb´–»yCĚ Ô1Iń=ůnDhqńŚT>śiiRmқ榀#©Ę)G^5MZś‘ŁëźÉ®G(X‰l‘ÉsÇ«ńű4 •6™ŕĺ„lj*[ßĘ4<ĐŁ¦([,óČZ©Ď8Dţý5Î8Č˙šßF‹VăĹšÁ},ç…ĹmH.Ó€oŐTúObHцýuQwóTШŕ<A,őČFA~1ß֨¾.ďa˘FVçhŢ´ń5Š.LçËŚYŕ;wŕâś#G[1ß™,jřp2zĚŞ·NýYJŔČ_ĐŘ3A-'eĘž"©(űÉß_JbĘÓÉ“4?-jäX”$=Šo¤ŇiÔőŃÎ×ńp¶ŐůA‡ ĐzÉç<2hŐ?@…2—™ńÍÉ™±YGI-‰»HúQ —%(ߨň‚2tuěPŞá_ő5OŔ¤ŻâNĐŹRzŔç|ßҟŲ§NäŘ××ú¨‰«KŁľŽLý<=ىűJŘóŕ%’ëškß×`&?`D¨Ŕz‚Ź˙ RľŔj^_.˝‹V[iĘ„V¬rE!ßh¸>gš°Ř v¶Áç”*ě»l#¦â Ú'»ż.Čâšk)Ůu ť¨đ9g€áWžX‰ýŘWů·G­Ř<–j…©©ÜăpŻŘé˙ú÷·ĚÝî®eqЉŁř$âLSňöéçĆç=(Içüđ™#x:-ž„˦…”YÍĚäŤg9Ť€¸??żŤ$ćE"Xž^ąř±ŞĹ6'›Éă‘ćUÍééÍx &Ěçüř"Ě GtŻhÚ-{lPĘÇç ÷Ä 7}§x”#{ŘČÇčäsŠ} }F´qX<©ďŰČ»FüM!âýÔwßzźwU\ŮČ!1ľe‘|Y˙eDA·§Ű®+`9ö3ăĎhŔĚKfQ’:{]§MQ%ú4âLpdDžٸ6čâr0•EB]Č”§ýműăĺÔ–V“S˝áGÔů—~t57'ů2ŹśŤjľ>"ńšUQ.đ¤3Ľx=ĽÉ iĽżö†qÚ]Ď!hq›ńw9Łײç:ô“Ű Z͆°:MĎŁ1Śž¦é2 nílD•´6‡iôyRk©Ť›ťŢkb’ ’DŔ˛ŘâÁ8'1ÓíŘ®¦$€ţ¨łnE›ć©Ľ¨đ3łÄȨ+T™Ă™¨v ËŃÉ^Kjd;ĽĄ`®Şä™Ňۦtm;ę“¶ýAAA_m ÄOÜgMs¬ ŹčÚ÷°/,!B}̉Îh Ż Ëo4€„Mj™°Ŕ?c†(áhżĺŤ](ř{üĺ×Ů“ †PEáČCC»[۲WsŠ0“aËřéÚĄí^;žŢ¨yes#$ŃLęбëŻńçKŃUQ7m~ćöÄ䍄W3sHb‡‰ĘËjć;JF>Ťţ8U©»ŞNT”Ů$5dÉ—Š´ŮĚRČä¦d»üÁ?Z˛¦fş,żňÔZ°ĚÖ+kN~Ş7;Ó$!ÖĄ«üúp­—“Ň´T-ŹŻĘVš+iřJŔ=Âýů´g·7Ű!ۧ€!Ň 8Ó©z‰–ť„ş Ö‘”Ě ŃDAĆ©†ęÂr•„´†¸Qw)©V>q{ŐŔĐ4;W‰´ŮŠ”ţÁ%‹Q WŐ˙9O8ů P4Š©şţ«Ö®e9c˛$ÄĺQŠBŹ‘ô¶ősž©’˘ŠaC- ăIÝ÷4m=˘˙ÇĂź2»011ný©  pňHĎCšTi+uGMĺ¬ĆCB¸˙iWć}†` ŢË,Đ‹|„pB‡,aí \b­ú~đŁŕ€×NOĽTH#´ńĹň–“WĹX»d]L8ü˝°0ž'! «ę]Ô3Yř>*›ęĹxKx!€*»oÂ(¶ĎˇÂá›Đ'ŕPiS=+űU“ZľQîl4gˇWíö˝ôÓ’@µSől$óáúGť— ‘–Ď9ŕh]é±€eŇO”‚Ńä˝@1¸Ľ¬×ô_±X×Y°šayŮš3s]Ă&Źű,Áh¶5‘Söą,1Ľ‹‚{®yo $;ťňĂdĐąŕWsnQĚ$éźVµ÷ا/>Ě1Ęd-8v¶Łĺ‡Š«Ż,ýMŹ öçŚá­Ö܇®ˇn&}0MˇŕśÎÓÄë·A9Üs»¸š—ăb!Ę$ĄA€Ü8çęQ!QK4—CÍu÷?4\Áô¦6Ý!Ç&uΡÂWżA‡˛á4»‘!l­úm9\ĆŐ"ť«Y… k^¤‹¤OŃ„vmi÷=5»W7ź €Äçă[]–Q2ĂT"»ź§ď2ŕČşjwŔ×=:6E6!ŃüÂí1Ót.~ŚČQđł©sęVŮŮmQ«@ľ;ÓşKÜü×.Ů!@Üa’`_©Ćń°qźôË{T˙’ŰËź_ÍvÓX%Ů%ë´!':<Ľ5¬®>ý`K,U4d“dd`űJé‚çŚü´7J”˛łťŃŚYJ/¤Oęfţc+Ř;ĺ‘ĘmńŹm/ zpCö9$h†ĺIČčž] ţţ‡ŃeBh‰ĺ+ÎŚ(łŻâęŻ'‹żË Ă §§ő‹łŇ•XŇV¦ňç¶Cbˇn«#‘$&7çÁ¸ZžޱŽnµĹˇdźąWb}@–Ť¤ö ä:4ŇbŁ‚Z$ !•ZÇ eÄíśIýš©ĚŤçÜÜŤ â*ÉM»ŐÄçŃúÄńăśg’± ŹŞüĚP?_™–„ ˙ű˰{ ş†}ĺ ¬ ëgľdđC×OşŹŰ=¬YNŽ’awý`÷ÄÍÝŞĹ`÷ÚŢÖRíž„¶űtÉ XŰů,ľ,Č”ř5éÝö‘ů±ĂÔçs÷ýÉc/ÔŮŕîsn%w× ýIX§q„–/ĆćŻwĺîŰÜ"’Ý9Ób8AÔÓ§‚NĄOĽGäZnĺdáŚQŢíDżč_ť»{·$ź.‚Đ7Y ď{ߎ (ﮇ2ßI˝Oł-ĎÍŞ”ŢEÝ@/"îőËĹđ%ŢŘÉŻĽ÷ë7pĎż u˝6‹:µ ąLN±fÄčöZYa‡. <ꦿšÂ ~Čă©ň”±r}9Ćůâ0ŁĂ˝u™Á ±€čÍá…x@ńîD«_"B °€şżŚĎ» =Ąů ±hÂ% ú‹taĐň˛gDqË®ëóŇŮü\˛}/ĎˆČ)Ü“]/nË(|QZp?Î ˙Uz:‚Đ,ť Q Ç«1ű$đď-†xOĄŃ$p.·*sG˘D©™{÷űă ę—żâR”łĚÝ;çě™áâ=l Ń”i&ŽE˝Ŕfđ©3™3ď÷•¤úż§ŹQ”+™S¬ąÓ<âOmżk‰ŮŐ{V2čH˘…€DgĄľ¶j‰SŁRáQ"‚;ŘyÁÝH݆ś+»űeN—ż·łń‰¬7dQľľF5D ˇß qTęBÄZŘţfž„xŰšUfЇ šV{÷˝(ř«yyŠ' cPN"»Ŕŕž]2o!ťtÄǦýIW¦Ç s·»ĺúNdE}2łĺňŢ”ĂŢ@ťů›sËđť!ȸýŮ!OÂ.Hcü¶ű^%nĚĹŻnż‰’*N=YLÓá9kpA ™Ď{ěÄĄŠÝ3eqŞYäěvv˛QçĚ_68)Y4.5«y.ĐłĚn„„Ô(…qÁ®Źe;R­PżxÇ#f¸Ď—Ă-ŘÜ-„é˘[«Y±XÂŰD ;eÚVi”8ÄýLÓ>N"M= (“zaěĹý»! ¸ń”4pÓ2şZútéŃ%ćČ«—Ëvסŕ:`®ţÓÎA¤źB»Ct‹fă$Ba Ń€SF.Đ7š0wS'qäéĂł­˘ÚňĆËő®cž¸«÷•Kđ3[b‹ÔčbYť#éÉÍ?…Ě-V¶ ¸ŕqď­ă‘Ú0 ZÓDOQfđ9*G®CU$z·Đ+⬙bx´pűîGg]iĚîˇL É:ö#Ud4Îź÷BHݤ*ĽxëäőLÇŚ»ęŚ«‰ŕ_ŕlA™$@ŢİeUÝ56%”ýB=µ¤ÔČpču•]V«Ë/[6ŐĎ kŇŃĄĽiŇÎX…ŚMɧô°nCŕ´‡Ľ4^¸0c.Dcţn…ĎnµČˇü­®Ľ´Ű W®'Ľ´G}»f¸O0k7řnp×|Š­Đ\·xšW]kUţ…ᄀŽKÔyă‰kŹÔž‹D¶®ţĆĎľŹ\„…*ήĽÎ›D\ô“ç`ëÔ“%9ŃŃ›¦“M·ŚăĎCd^Ě.đ—b‚şŰżuśâ2M… (Őí•—g\Í.%śŻá=lŇÝ‚Äy`Ęki49ýŢł±ň"Ϭ˙ÁrfruänŁ`뢉|¶‰ěĘ´‘ŕĄŮżd–‘ Ç‘í7«‰i5XęíV ®ÇBţ(_ľ ř˛őű2Í«hAťu7 •‹Ď}'ťľÓ- ˇ<\Y‚iß>aśŽáĘÁ݆·ŠŁ 2ЉMĂ“Ç]ËD.x-úgŻóő–ŰU˛ń:Qg Żč„ęrHuŕŕ˛/"´Şog=nmĘL€Š„ľ?…ůqt˘AłY†:\Ű«d‚0U3ř^0¶oă±ë4VßRíÎŻŰ-v7O|$š7Ť?iŘamW[ÎW.Ëű6Ď8 ×SŇNĆóť‚@ŰŮQčJ·GNEłń ŽŁÎcśsű#éÇ~Í‚ëŞ;1ŃNA[Íçr‘í­»ń]‚í=c”>é/mŻ E}ľ‹ÉHÍXMZ㱠ـĆmŐÇÓ&Ä$ą*5ň¦ˇo“"Đ‘Z ëžeB`¨8v´"”üU7S6Ď"u Łpá~i#@ܲJVzĆl™)P\č0â¦ć¸I°áń`´"$„„0í„„ÄP¤oé¸WÎ&5Đ6Î]‚ÓÉe«3ýµĚ>Ă|ż§ś˙Z+ßśß`;  Ýsn˘ˇ±ÓaA?¶Ă×čG4Š®ôBńâzÂŽżx?eg 6zś_Yĺ_ďő¦ë¶¶Qt°DaBmhůăűµ©Bóů ÄbJĆ#ôMe·úó÷Nf»…\mâČ€4¬©”ąÓÝ ,gÁ¸€sÜMÁ-rÁă¸ćE°ń3ĘŠ&ÔŰĂËŢ”±ŕ»›“â&–‹ś <]çí§8=ĚëOýą$.ëŹ)Ćr„Ł8ü{¶6—ph{·6ŠÉŐܢĄÂÓŐÉé "BżŻR¦J|IăŘ«ĐÔQěŽ&ů‡}쌏 ŠČ’i[h,”(8“Í»U­wöp;őÁŔh¤ť—~áŞĚĄ IŠmżh{űĆítđWACpÎ…ŁŮÝp×Ô6.®ßP»ĘŢÁ╦KS˘µ3y]<îz•T kŘéůĂi°™s ×ÚĄ”¨8ŁĄxŠĐ ÖdQm„$F®ŁźĚFv ™sWŠ.ÎCJ¶ şýt!ýšĄ Ú ćÎ!\\ň¨µÔ ȸ ěR7Pď)U7WEÔ·%×oÎ_KĆ©ź—EâěĂu¦ćkdW™şLzŚŚ‹÷›SóGf6ÜoKS–벎€Áţ^UŐq`f.*ÜŚ‡uÍ=9$»QŁâMŔÖ®…˝¤fÖ{S’µ)‰TVů€¶¬®Žt­^űÝmvíriýłž1 r¶‰ßÓf·ű«öĆëVľ@ gĽÓNbCî±őr(·GÎÎÝ‹•b(ăőG¬A”ůÍ >űLËëđhزq—Ëýß˙ěnBF61đ2ń^<ĺđÂH-‚wúďŇŞUNAŔáHoäĎĂłe†ţµGşct\6¸Ý”ź—M Iç7ś0”ŚQĽ QPÁHü71qŮáFł>S°Ł÷„Ý ]0óOµ7í.g aŘ)TMöĘP8]Ą =PÚ‹­¶ Ż!Ą¸3.)’ëô1q7VÖ&H^‘€fŻ–É4hňSdĘrfÎőr>`$E'–NŞ”yO_Ż`Îť—Ű’¨sĄázB îRŘ’§RŠc93p‡U/@.ׂo°„Ć=¬ďşF€ˇ‹`5őćéiÚŹ¨†>;ň´_Ät%ŰĂL¦ÂŤúp¬ţXA,ž˛Tf˘­l˙í|颜_I,•«UwÂH-39ţpúać,Áížç5‘ĽećěĚíŽî(O`ć\ÁíŽÖ¨ł-׫p±=kňFÉľä›Ć:ŔQ™Z´­÷tŐŚjU¨˛ú ĹĚ…Ź×Ô»Nśqʩ帚ˇÜkťŞ^ő) 8‡uä©č&X`|A”>m2éFgćŚÎ;Rľ qhîĚŔ;Sľyaî,Á;“«DAv«Ĺ q5ć™§Rş-ľnŻČ÷ÝŃd ±§·xŐĺ™Ç«N›¦¤ŐŰűíÍ–óÄíIĎP4oŰЬe8 °AĎ·Řc[Ů«K =Č–9gĐYV° HDDO!žµMHÔHá é &"R)™o)IŠ&ĚJfC[0 B´‹P_őËÎ`4ð˘{HHq˛ĺ}„Ř7z¸Iąą}„Ę7şxI…ĘĂ€Łáúě)QŘPJ×fá]䲳2°kü=Zv"eŠv CˇžO@…6LKże{@;Üá|¶ÂÚô‰*Î~ď,%ęÎÚ|©@ĄjXŤx6űáČX&şZČ1ąŰ‡ˇ|˙Đú“ „Ą^H˙P{’Zd3łT+z3Ţ(iéoAůh "8ÂôîBľ@ç˘Ě7¬ąBţHgdS¦Ěëúě‰îwáńGŰöš©Ă8DĄŃŚŠ|ţžŐJŰ…•zaĘC¬ˇ ĹÁ–§ś+ęç«rA üÂ,DV¦'ĐKő|††PßZ€ň-DŃ9µ&„Jő‡‡"piTnŁÜöŻŮĂÎDj×ÖŻj’ěnŘÔ{Ĺ5AZbMŻÚó&zé^ hGŇ`:„{x,E$ĎŃAîńá1‹°˛Íő\MŘĆĽľě˙č\Ř=¶+Č;J„™TkăŘűřµ7űŚ<ô¶{ôđĘ4ô{ö 7żŘÔÍě;Ę=Dj!^ˇ¦ąËZ Ű)@˛÷sktŘ| ët ôŔ\ż»ä·–gśGxWwWÎďu3©Ť!uó@ͶđÚę üő˘áś;LÖ©×w;óµ‰+±¸38­]íŐLĽXşç7ş9U/ľźä›vžáśćż<ýŚ+łM%&˘Ź™­Âa^¦—¦,„Ç™­ZĂaž. Š:™Â¸÷z–ÝÎSĂřN,áé5«óPżî' Ćßü„Č_*˙űś2»P®ôĆl5^_Ő9@ÝRMĎ Ş˝p[ŻěźG`ţdľV†… ÝĆ$ŐzËţšOľŘäÄĘ‚Ń_ŞPa«bmÁ€9‚V¤ąkµą¤HśTů«y@««úřŻ×ĐtkkŻ,©DÄ8°k1wŞĹÄaMł|6Eú€ŘÂř@MĆS?Ĺt˛´Ä›[¸›˙ďŰ‘ăŠ-s?o.ł<ňý[Đ$…ë€Ű~Őă˛7ÂoĄIie %ęě6ě,ĺŕ xxáEôiÜě6 ź€Ĺx9˝wÉłhÝu7m˛9Ň^ým¨µkµ=óśşŔ@Šśh¨sŹLéW;TŇ]lÚg{€çôżY±XŢoЬĺ7[Űň|cľP ťJ‘<8gĄÓÜ-čs|7—ç)ĆçÍ˝ą–y5ĆMĐŢ…íuN|/uŰďBČŮ]oX¬Q –ŻÁŢmčvŽ$ŠKýk Ţ5s­-Šo}OŤtś,˛vt®µŰĐĺś^ź€ŕîU8o…Ô¶÷âŐ¶ Jw„‰ö¸ 4!)l2ŇJ 0žŕś~†©r·ëÁĹmţa¶6xůlWť2óđšWIŻ×‰‡hŻ©+ÇóOŐ0řt{N`őĂ.’rÔŐ>ááÉN,B…Lë7w"ęíĽĐ":GdeŔĂiL˝®ŕSNžyXČŕ"·ÝÍf´Cxőĺ“ęEî1&ŃƵ´0ÂCjtýΦ<_Ę,*‰l›§;'tO:3p"Ţ毿eĂ}]«N˝č.ÍŘôašx”t7ް!wÎ9ήÇ:XB1gţü3°ABË*ŽňĘ •ô8Ľ[0,Ż0«•`࣪or@„–ĽóŢ7 OŘ^ŢËČFMěžy‚ŃCaô‘Ě?g’őöź‚a BČ'w 1“-ř"3MuYş“Á¸=qžM&4Ź®ˇpFH8kł3ÍCíNş§Şë&?IEdMńsßÜ ˘žpĆ’µ8 ŐPŇĄşćh„ޱÇ–’ąÖ˝+:B‘ŞËŢťdáęöÜ éÎ'9pOGÎ⯒—Í·ťµ˙m_L2ć¦ßúqµ I €ś†NôÔ"I |—ŠnGŕ>™Źx ť€8ö Ż»ŢZŐ˛ °Mjč\ž÷ŁD:¬>–ě|¶”l@SŚçžTU6ěöľŹeű\Ţ,' ůżĆtF:č,ZL7JÚŽ7 ;…¦ŰçUoo2 LÄîőWŢ„†!Ňj$ˇő×ĎŔ9OěcĆËc¨Ô)Otq‡VhűˇłŔh1'÷t#„e8ÂDT5w6Aĺ5©Ą Ő2ŕLx®ŕErçš +g5b†]·mg ´dPŰND÷Ü-ńI°ř-CZďá$8ĘnÉżCT7Á˘ń[ł>dn‰”Ýť„Î3ÎůmB}R♪ĺŔ´ˇPĄ˛eJ>‰ÂرËd ”ÝŁKcBŹÁáWnt“úµmW™ĎçHń[:(\ĎťZÚ=hF˝˛gŠ>´ŕo›__ŕŚ;d¸*>´o^ň-[ĂźíW¬Ŕ˛—*>‰IaÇ·Hß=AAľ÷é‘<-‚Č~đŢwňШÚ}Şa+0,Ëb÷ëJýľyďłý20’nɦőéRý–ůč3ý2Мʦ}E@˙&üěsů2Đ<Éj~Eśé7|öݲ•…iŚ}d¶aĆӇޒrîT1h_ëw…Rh#&ť…ôްő»Ů۪bä:‡ęÖÁRî"]˘H= čZěűw|A$Űśé–ńľ Áh¤`_ÜkŇž=Ý/á_ö7¤/<¶÷ â댣gGü»$‘Úřň,őj@Ćć~yśś őě¨ü`„ŰşéŘqĂăč1HÉr˘v®´{÷ ä°6ě“ŇBDFLzKábg§Ĺ0ĚťôíH1ɉ`.¤&D Ӷ㆛MňÂÚ°čŮńľ ¬Ä¤Ö˘D†~ŰëAĂś_9:ΔúväąP´˘R4_9Ű ÁĂů_9Tm?é]X1Éw!iĄ¤6BEFŠz8‘bsŁ ®Vúö|żB~¶1Ň?Ó}…@XKJń|·ŇSŠ;ú…•t×·#ůjÂZLŞâ ‹ý]°ţ+‡° ľů{żGBRVÔÂ79=jYŢB+BŔçúĹŤ¦č ”†7ÄT·029tŰ"pKM8”BŽů|Ž@—~…-s†®ŢUŰŠŤ-BŻ%gí`?|eî‹FK»[ÚŻë]RÁeďű÷•|Ć _ć™Ĺ/yňóÍŘčł*hä‘Ň’0ËŠd¬5č}¤a”sUŕv—çŞZx6@‡ ŇÎnÔ3–e·ŚÎ$łČ5Ľĺęą§¶ä+ęŤJ`’B˙QČ=d#kSřM?Ů=w«Ž{+>ČĘa 2éVHŞźńcBđjËɌဠMy:˛`nß ¬ Nę2Ö‡:i– #ĺWŃQYWîV±Ú=pŞWr¨EçmŮ]ÔóĐÁëkf†-gÍýÇFˇěZ°EŰ,îK·Ŕ]Ń‘—ćKŹľ\˙-Šd8Îś$ĘR¶”Řt®äZůPdZDÝťó×ŮUąô{bî?ť„ÖNę´®ń`(6µkŽÓą­\a¶î"[ôŹsągF)hć _ż#˝Úśë'y3§ťńŚÍęcôzÍ_fÔAXnm3ť (1–ŰPsí-űZs7uďŞĆ!ˇîxŢ-(ŤŇĆŰ`™ęqflÇsoѵŃRV Ě\ß~łĽ¬XIńşÚ§ńlćŇ>špüąa™Cúcž{Ö*ˇŕ6‚âŔľń;e%29ŰýaK¸°ŘwČvŁ’?“łlČ—v ¤¸¸ey˛T´Đ UÜ8ţüčŹh4OE vűEá¨ËZ„Jq EMŞJ¤§&{í35_'g©nÚÝéôăeMš8Šĺ˦°đ”ů,X¦ ť<ĄĹ0páqjtC­ĚWŃq:YB ňČŐŔńä€U< —]‹äň®-ÁűŠ‚Öy\F%z2óQ5{ͨî¶ňcí?ăôµ8HÍŻú"^N­ł2Ź˘nSS \N®Y„“÷XruŰťłľ(ĎÖŞXÚ\ÜŠÝgǷ
 ËQů¨Úę7¸KwAVE‹ĹÝ…Öý‹jĘ7ňKw&îZ¸$Đ©¸Pµ6:ĄÎx®šľ÷ ÖŠ•«{D;7ű ‘¬ÁEÓ0dńfĆ?5™ĚşČĄ[N•µaR3"ĚÓĎ}evŹ,mĂwÇQ-ďj–›–ł1iĄ'Éß(rVŹdKţh N‡[™c˘ŞÝŘ-ÝĄ9‚p„ŃÔÂŻ—ń9Më ćjě; ¶s:`xěW(+mÚJń¦w í‚t\ü–9ą{—P?ą*(•á-ĘËţ¨0Ŕ=˝ë•X1uú© Q×Ď`,EË'SŹ}‹»n§p¬Ni+łžDĆŮŽ)-n¨pl`>ݧČ—Ń€+-ëĹxlŚź¤˝¬ęşaD,Ç’¬Ý¤óSHiUzO” W6¶—ľQĚׯ‡ýnŁRąíärđ$ĂŃĎ)­ßry‡4.uҶU8·`ÔRéą°^ŐéN"<ňHAác]ë aŁÁŻłIN<9 Ąn–ĚÂU®vĆ>žÓ‹s˘ě÷#[‡[Ipm`::㸠X(“] M&öw°…$†ď–Ě,h•8ćC±S>ŮŁŕ’$fE‡AÍ·=âAIP^ç(3 řţ‘ÜĺťËknµ[Š&Ł9u<öjëµF–ú6ŘăęQOůŮíŘÚ™cö¸wčÔßfáüoW_2 ÚŢk–+(č„C+ť—9_{2®ĘÉűś+ČĂřhIŤĆ^··ŁÍÄ#ű4tZ4 X őú&K˛÷ 'ZüĘÖzŘ«2Ą–_Ć#HÄ еQNT§5_U2‚—p6!_ ĆŁŻł„‚· c®¶Tמj†ýćđľ9$’ż1±6Ů<äx€fAąˇçtG¶_Ş®!ıw"+ćsÂÍĺ^oܨŚŇďˇÓFzHhIă‹kĄš~ŹO%ęX1cbˇ§ÎĆ~ľžDÎC!ŁĎî8čŚ1ÔttżUÚŚŘDŇeĄ]‹&MĽé[XţWĆć‹´•¤άz)´t5X€őä9ďčJŔ$÷lÚCŚ‹Ô}-­ X-=ć¬ÜtuMî—yYúŇf×»ôűż^7ŤQžużXtłĐŽůR÷7ŕ†Ž95*:^ ¨fś´ 3ŁhjwÍZp.§M× ă~9Op9ŽÚił¦ŹŚ¶fš%ϰßöQ“ă“?®!JXî'ÚĹo6iŚ®Ť'{4âăDśqĘ% ňÜĺw ë1%?Ő­~8ądoŐÖ€ÜÉŮ]©5aŻVWśUýhvĚ)ü$H5O®g·/ˇ\ń%ţśXc0|QâĹvľH(ŤIFKa蝎ŕYŚĂîa´¶(Đčř4´Ő&6á\ÝN(őé¬M˙а4ne" ĽŢşµh,x«áOjb_Í%_5vm?k;/KO©ő؇ĄíšYţČcĹqfâ_T ¦Äśő7ŞĹ‰‘ łŮř©6š6şLĐ{¨eZ?V•¶źj'ŰHőŁÝh"ţDÍŻ9®¶ńĚőĚč/m®•NS?¨ť#•\«L÷é­y2!;'.ŔK8–x‰pčÝ€?K q24ľÇuY81wAÜ;űĺŔę(‡Áźh©u÷˝TŚž’ť1)|őł[q Ě€Ľ'ü1ÓˇűyąîJ¦2ăe4ČQ×ŢĐňęşiéĹ“  N’†ŤäĂŁďÖU7eŰu]‚iLm( ÇÔ.?{Ó^1¨ŚÝĆźíhK˝™˝3nŻĚ&¸©áĺ×ퟡĂă`űzł|“6}_Č@@lA ¸°:÷˝d˙UE:[v¨E‰•ň& ßQRţ P öŞT0;ôeňµ8ö¨ÚÓ—ĄiÓ7Wâ}‘*ňŚąź1Y©#MmoçÁLÚ0Ö0µîÔ:„Ü­(.kÚŁŤb“VľE9‡C+ébĚJĽ1üęT–śj8ö‚‡#·6Ůťú<ÖuhŔ5®RSňăŢ€}&†ą4̤÷ĆĂÜ#L3ěť/ĄYśr•6NTă&š­<{TMäĆ›3ěÂť]Ôpné]Gµ¬?aŐÇďŮ);¨´óFš=Ý‘.)íź•Ł˙2Ů;kHC„`\ ô ĚŚŤśş—Ô4#gŕföR«3ű©—ĎvąÁś$­0=ăO˘ßżrĐŽźä…ďq)Ä<É»ÝÁ„;¬‰Iži®ĺ6÷fÍý”ONpÇ„ßUbÁ´,äÎ/ĐĂĐî«;Cx˛Hy˛,YjŮéËjľá@ş„@Ë`Ý)çý3Ây}ökk˙±"-űţĚöiżîRMŤ—ŽÎŰř[ĚĽěOăş=úKRŁ“7ąAAsćo· Č„˝yĂ?RĺÉ*—ŇćĹ;-¨łF†3¶-Ăt·OŞUhąŁe·ĹÔŇtn†¸•*ŚO•¤ůcŁu 5ú,ODöŇn×ÉĹ ú4}Ř1.mű 1ÖŇfëŇ riK§JmĚ˙E‹sp-ÁK®U×¶‚ZWŤÎ°rcş,—ąéśZľ4Z‹EvŤ‘úŁGyŮďąř±jąN$u˘SŠ-)VL‡z[ ăĎUR&DĐ­3·"#ř1f ť~Č7n3Mđť1Ą%¨C‹ŤÂ.'ř(µÂÂ\‚î9öş@ó^.‹[1Ęď„Ühíqŕ6M&:Ŧöłj’źżŕבo ™nß$““hHżť˝w„žÜĚtéĐS·#®őÚě×Ý›-+o„1»ŹwSÔHo¬Ńîíß˝qđŤE§Ć˘%Í__ąhĐąm LU±)±„+?»ëŞE0®”3tyB˘ YďÖš,0)Ď'ѧMţš»¦röD12tčfÚŻ»¨ l,vOĄ.7&nźÚ®€,ż‘‘)€ňOł¸]d¬Ňţi­ëbXµ)đEYCDă~Ý-ŐŢ1én`甲ʧj– ÷[=µ?‹K;EqRÎ °W‡c3ˇ»5çĂvÜDĘŮł-+ඇ.€áy@¬mčMm2ÉE/H–HÄžřhÁ_Ă»Ţ95Yş€‘P´‘&Ś0¨Ęëw[ŞęEâţŮ™m#cͱßŰn†ŤŰŇGrz¨4npI9ą6·Ęd*”ÂvFšÝKţU¨żé!’ëEˇÉEĹ!u˛?†ŠŠż^äd‚ ÓŰČG†=s;ĂCK{łüŻ„LçŘŽĺżđ{čs4«Ö@ú6íŰŢŕ·=âa„YO¨ŠD!ýŠÁ~#Q>ÉéŔń”(ŤňZ4`Olj×ĹWdOŕŢ·´ ęMŞĹ gŹíŽWnJ†ŠBqč%´+,ůiĚ"ůgÍŐxŤŠţŰĺ_Ţ3{p8?_Ř•Ä}D­ű¶ĹC 0.énrą¬~ڵoǵ?•bęfs9SđďőqtúŇÜŻoÄ;pű~‚\r˙ęL›[‚–ëm•¨<äĐÍv_0ô}‰Ć[Őľ}‘$uˇRčCjŰ#N7T´m’SuĆ6J—ĚhRrnÉ8őóoďňuE0ě`Vô?śďéń†˛-Űq®ŔD…,šІ­ńŚJ´)†"ÂĘđ‡Śµç8¤ncb‹Ănn—+Ć%'9…y…Q?ŤţEfÁ´AµżŐĆ-¬ť®3ÍA¬‚e +detxŃ+¬7ÜUč6žíÁId®ĽÚYÖ_·V†ŠRsé˛Ě?ĘPˇ‰jťđ>Ct‹Ę9˘lGUµV $BăŞĂşsÜ96q8c\-ůúDÓĎőŢ•?­ßŘ.ą8oĎ­"ZĂAÁÁŮŢ,ĄÎórŇëťÚľqíŞ-N ΤշZ°ZVĚŕ‹×Ý×ď©2Kôc,MMŽ˝ÄζžíčÁěČ´ü“ĄÜjâµŽŻ˝?ŕ“r¤U´G0¬ÖM}’dyÇßNoÚ[oxµčĐÇ’˝Yč4J~×۵ΦƔPÎU&wÂŹĐŮťfUDňŞŰpy'-Ő}÷łvČtčŮVžŹ(áwlVÚA‹)’ř9mď5čŤ?t€b ’Ú¨ş/^Séእ-An©¨$m‹«#ˇiţĽľ…š&dÉŕ±-Łs¦Nm;ě$‹FtÂtëJ Bâ+˛É"!KgÂzyĘ ;—C·f˝p‰ŤÍOQŽŚýŚ v11ÝŘDc˝$7lĹÉ?Ű9}¬ SףŰOU'äˇ?Ńş5'ű ť­ ČőĘűÖśb(µc5'Ë&űŤŻťŰţ6d@d…Š˘l=Ó¬¸ůŇŕš_Ő×ÝŹÁhqóy‡RÓd PnÖߏˇkq•L:ő.0j9\si÷zú[rĎ ŁŃî0?±¬ş„ĐđDĘ_u,5d$çH˛rCÂVč@©@.d­Ő2׊&rPLgŐ'K¶Ě©EXZ$ĐüQĘů­‘fZ ąŽRb”ůľźbÄľ…â)O Ňf‹­8şőÖ^´Č{sp—´uď«*ĺČ®ě‹Çî&–f6oHĘrhV€ŚčÜxR÷ŕ Ôc:±RľIäš"§Ž·­¤'cuě¸M…đ'6oKBÖ•XpŚ)€¨ÓĂ3r»'o¶ëŻę°Ďś© Ó˛/ś­˛__¤©Qîű€¶ ë -5IJ=%Âqi‘c‡JdţĽlě§‚˘9Łn§ĽřP׸…"Îyß-((ç©JËÚ.Ix˘čäOX"¬hbħéěŢđ™č‘ŰŔÜp ůýî Ó)Ůö2md°_D.É@®çĆ’*`ǨBĺŻţóř$fÍ÷¦FÝćO×=hjÚđp_h X íßš]ô¶îéíĐrť±§§ź-$ÖÝ»ĆoaˇÖJő/@V¨—˘4"`'Ú©R%´ů ˙"4fŃő÷´S]–ůë†˙ o1µQϤ/d#+ĄÖbd\"‰X'Ě­Ú‹ްlöŃLŞwŚ·HŞť¶ń"M.XKµK=śAÍ-ٵk‰Ú5t%6eÚ˝üůÇ|8™Ď¸¸:o.­SŐr¨ę™oÚ• ÂiU§-]úÔ;ÚĆéG€-16"ty’źÎVŤ'é8ńŘxŞˇ,{śĺhˇĆx×AŞI”ŁdS;<ńőYŘ%ş ó˛Ó¤,Ҳš<âŹÝ^Ťt%ëˇÄÎfÁ™ó0Ĺ‚@”ś"¸Z™:řÍy6˘7s±?Á3n1ťŞ«:üŚ•D4JÁ¦˛"Ľš:řŚ…Rp—ëtO”¶9t˘?j@šşŠ^ŐK-~Ţ´®Cm§ę$µ8tXŢ8]ć€ę©`Ś„ O• ´·xŐ9€jÇp śžx9Ϋ­Ä…9Ľ|sžŮy‚÷…$Á[ćĚ ˝ÚćEUG€{ĚÖÄ:Ż5}MŔ>Ą2Ó¶Y¬˘şýŠý1ńoěĺËF”áú÷˘n4,!ß1Î&M%o±ńŢ!őŃG!_©{ĎÚn^±Ĺ¨˘É ±ĐčVU­x$Ô^:Ő\“‹SR“@+be‰©RV>@ť^b3iXŇ3&‡$ MI ež>¦§‹řĂx.Źf˘Qá[šs5}Đą—¦s©s‰ńÔE+Ó·”ĺĘj€â˘O¬OWđCw^ O%aNÜ~LÓ}Ľ.Đĺ4¦t®v"ôüŰ ěج$đ<@ŚĎ›ňg±.Ń•Ž%y·‘/ńp~Sx ŰĆwÜý˘>¦Ëž LsOR<–üś‹Třvů›ĆĂäy™‰űbřżýöUűŠ”Ą&§+üŠÄNR9Îő2ůXý 4ýä¤đ¸;ďĚúr<č(~;>Iâyöę>µüŰ~ľ+ľn* )DÍ~<*˝Ěđţ©űúÇ)X‡’źx2ä#Z˘ŽO• TĎ'ZVt—Źv:äŁôbĐť"ˇăZAĄŞr,(jńsOíŽP­Ş8&7qȇ:{RŐ˘tĎ„W­*7&×Ă »B`꼔 —rbh؇ Ňë‹‘QâîŇáZŁ«Łô]Ô»Äý«aAĄçŢűşOĄçݶ&÷e^ńŠG?Ę ß#•ľµĚ˘‘Żax•âiŠ ß˙ŤČ,ý¶uĘ U—ËÉľňŞ ç{ŮZ­â4 ˛ď?zTż Dö×T’Śy ü¶->y H=,‡ŕúEE¸žĹ$ËĂj°ulá%źçśěÖŐe& JÄ™©ÖúÎrŠyjˇ˝‹†ĘŹŮ¤ú5ĂČď·»Ż‘[żT)÷\ÁťI«($|ßÝľˇ%†ľŽoE[ΗKV{>™éhČüË•7ŐŇť]Ě_cń“>*’^ßÝ·`S~a‹é¦ákC…_/Šî Ń.Kîň3ďv˙ŮN2 Źă\ŕăŞďťs_RůÚNb1˙ďőŮëÉťá…Ů*@çĺÇĺRřÇéMôí쪖Ŕ{ĽÍü&ľË*˝á­|Jĺñź{UĚ~N} ýł¶‡ěúîmąxZ¬(v}OÎŚăĽ-Š_+ęŚ÷ýIé%Ş÷CKKO:˛qâ-·TC jO—_%˛u¨Ű§BűëÇP›é6˛‚ç«ä—˝ókY.ăžE˙JŕŮUe™Ë†E˛Žżĺš‹‘ŇzhőR°gŔQaţžö¬Ę:Mxř˛ć~µń†¬FWE4  ˛ű5›B'VUŞä{é2ĺăżşç-§Đy!~ťüg93•ţ±~ŮXżl˙.g9žÝĘgD‰{µlČŽÜwDgŃ·ÍKü«›Ěś÷Ë’˝™NŇs/Y’ÄzS/%żč›yďJü€ú˘ AŰŇ5|(¨r2¨G»?ú®H›ł ˇ’ˇÝEę;džÉůĆĹ"”,Đw‹őüč{"ĹČ‚ßM5đŁ á łŽA(iúÇyf’‰Hß&ţµ–@CXĂPJŃ;ŁŰä„Á‚®˙w*â.k |É˝'„m‘‡źj8qź7¤4 µ˘wĎź8˘ţ§G(?·†ŠŁýä@ §QýhĺL¤ ĺą0l 4‰ëöí“5Č›„L%ݧžY+ąřŰÄ»®żť kT0/&QzĹĎ#ć–¶şˇě° äF¶ľ"ď(#䆶ľ˘ě ä¶‚!ďÄMwŻűBÚţę;ÚĽ đgŁÔżłz¬HC ç¨q"°B¤o©zMÓöMýIŃ7žtř˛ţňš’0 öO$e_e:Ďw6|'©~“ęcžŔE`äYň=ń!ÓkS˙6vižÜ¨Ca©ŘÉ’4zižÂ­>¦ç¶;„Ů˝F}Á/ÇźŤL?Ĺťź€ĺ+ڶ¤ĎŮfĹ]\ÉE}ŹéE+ľcČ%cĂC6_‘}ÜSgŢuČôŚÂCŹIv¶Żä$0-Ŕ+oMţ»ÝMM €é=żáĂő±D 6ë|Yź ϲłb& m`Sn¨24i`Sń™SöÔşK˝%lj]vl颦u},Xĺö¬ŮŤÖď'r™W‚^]oGAĄżLÓŘîţ…/á:1ÓD~šcD§á@łG…[p čˇ!‹BĺăËÍئűÔJ˝un7&^s24÷ĺĺn€_śŘ’ž4ÇżEš\tčHörnŮ`ÄŤiL|K’Ë–r” č+sçR˝L†”c ”, i¦Ë…Ť#N‚Ď–ř1ćďÇz%T÷Ń&cú°şCßé§ábzćĄx€‘óHpş;Ó' Y-fşŮŞWĹ!Ęߤ9U©ä.]¸ťBç¶T>_…úXé:E5žôlŁA„™Yů–C˝3lÇRD[Á&żr”Z̵žQIéV_Ý+ÖDVyľ-{ał¸—}V‰ů˛řŢéřďĂ?óeů˝Ó©Ť#´Â¸ĺń.ÎD@i3ďúfiŻíGŃĎŻLXŁŻgţZŐĹĺâöF} çâ;zZ­]ů©ăgÖńÄŹ†”×ĺwÁ•Çž43+ËG8sÎÁdčŇćŃuŢÁ•[MćĆÖWka$Ń…¦ůµ— Ľđ<ب5KĽPR¸ě˝]gbŹ b"2B!brv qBń‘âźbĘ2˘3Ab7v¤1bB1)µôŠ„ wiˇHŐyGîĐéA$âÖđ!Ş3w2ÄůAÄŇh9Ż]ę*ö‡mźÚŕÇžmvĘăÔwŻ|k\ĄÚ®źľ?ńił”ě5Ú?ő€ă§Jš»7>BźšĚŻ™^ęDđí(«3;­˝ŞäümKSü/ň-OÁş~7GÓV*=Čp˙tUä­v”7 ú”X !†hŔX“xS”ĽÂw#|Ź´ěĚ”( #¸,Ł(¸?đc_®k>×9ÚMš™¶ţž7˘ÉH*áŽ7řę>Rjözč_¬,$Yě—Ç\Čqµ E 4:5WxC÷\”ţüŔ_ý ÉĂLˇđď˛Dă)Ě5°i¨€Ť=\§Ľ!ůĐÓ–‘„ "„dî[Âź¶ŔLÖ űă>mD>±ídżĘéLX—Ý0M;ĺ!q¤_2„írĆÇtdѦVó®\W÷Ś46b‡Đ˘#U«D §önnÁëî˙šş¤vúdtŤ˙üťâŻ˙Âa1\îq” Ć{ő;‹ŃŽ-ÖgA‘ŻŃľ@'…ĆZt'[Yú˘.kqŘŘ3\ś;ś’°°”ô2ăĂÝĚAô8Źd`ÖĹX˘ömLl%ś»<[I-•ô 6ąv6š0ŐżY/Ä`e~ň;Î ¬v‹˙á·jč&ŻźF/¶Ő°µQ׊ĂʵE˛µůgç0 ŐŰ­FB‡śh›kc#ěHČ`;a®š66Jů«¤ůťŐYĂRśČ7¤ţ˘(łÇAŽxŃg ć8'?âM]qv§>ŻŤ‹ž°ń41ŽďąŠŹxY‹dč'ę~>óŁŻPk!pAh0T[łłNp5ĹƦ{Vµb*]J(žť‹‹^G—Xť¨Ů„ş`®Xťáź_Ε­YCµ(Ě{ăŞómĚM%Iá&đ.őžžnR»éĺÝÂ/ gÄ#ĚŰ›˛ŹÇ¤Z†Y —ë‹m­Úú›ň TŘ‹-+ş-™[۶ą.˙Ŕ\…ťß´m27•©˛űrWv¦Tg¤Žň ˛Ö_"nυȶ´ëHŠhgd¨řpPL›1!ąÂ(âś” •Ďď[ćdWH8ěąé28_[MË9ŢgęŢËÉ ŻËýHw4-jd‡6Ü|ˇpMW€ř•|Ź˙‰2Ćbľ%¦»˛Rí-qg7¬?±ŕ Łś?®ĺDé; Ł”“ŔIKăB¦ÁЧ%ŔŁ-€‰©2l<€"?—ŕM‘Ź»spŢP Ń?žx€; g›śŻy÷z‹!m€ľŞLüSžMĺÇčͶDŐŠ\Żďńvęľl*–eŔ/?đÜ‹« ›üÂ$ç¦Y˘‘†ä¨®†^-Ü`‚uĄŢŰýí‹Z·żŕ Ř×!GĹŰ76Öjr1bŐ˝ű›ăĹůŠŮËtk6ť˘& č&XT,\r {5 L ¸wŕvĺđ]˝‚†üˇŃą¦tăWU*úÔăK¶U#WA˛ Ł…Ů`ľĐ›Ł)T¸ě\'ńýĆćΚś\Ńy©řYţí>T%ŰzgW~…ç$  +{cµóÝÉýqw\NŻâD©©Ë·źO®ęt¦G°ŽČpK`'źúË ÔĆşź¸$p2îG­—Ţű{g·ŮeC×|LkžłŠ‚hvŹŹ®,e«óÝŹ˙ĚZ1†; ’Ťë1–eőBá&›ú@µ…2µE«NF _N9Jj˙Ş€QšvtŤń‰NhPeŔ͆ݼˇr-ĽvçbÁé¸D(¸mиĘsxň5šˇu¦ăx›˘Ť¸ÂwxúĄ¦Ź:Ůď;ąmŮG•E·bŐO”5í\ĆަŽZµčŔަüÁmnđ‹=ĽţaĎD/ă¬uɸHK„IŢxM¦çZav|»@É´ÄŞˇx¦±TCŞŃʵI~9OUńPn˙¶Vĺ\r$b ޫР,.,ďő\B¨tݡ Í©5‘żw±oď«¶G8Ř®řÖ•wa—|öÔÄĂí•őÓTib˘›3×1–çItu~wĎŤ®őSráł`ý´®¨6? 58Śěi@d;Éä‘~ĆLćSIFL¦+ÉÁéĎPÎ[®DfĐ Ë«0˧µ\˘M@ľ[“J¨ČCŠ“˛ĂŚo.¬ćcjęP™Ăŕ%±@߀C%±BYźÝB„ɸyČCa[ş9±FŮÝühÜĘ<µť.LímŚă{‰›|ÔźÝBDÚż¦&Śş$2\JQĘ˙Bab\ŤĐÁ\L eéDUatʞe—Î÷őuS˛őz®§:ŮŠ´1c’¤!LžÍ[ˇź9©šĚ‘yĂVn„9U> .Ťi°e,SŇŇĘ"†3oÇ‹Ż)÷®˛|›tĚzŰRY´ĹÉů'„S&ďâYé?nBƇÂTt«k†…€L©'ËöÇTŰ´T†kZŐf$žwî1µŰ0’+U#kQh÷ÂÖ*Taəۊí íq«ç®'‡úC¸‹·ß3˘űGîmzCy‰$QN·ť X}D.”+R134"×JU§yŠ·M†ŢCP\ęz/˘Áâ5|Ή—ń”7&ÂnýDuĂÜ”Ž)FńĄ9h3ÍP´\gGňťwŹf–•i»ÓEDă7š1^ŘJ†îG ý*аҌi ń°ż•—˛s8ÎĐH¤·R–©3s^á­ń§ÄH‚ «±WłŚ‘B@_nŻkŕţIwmx^ B”^ˇ¶Q¨ŘkŕÍmÝGĹ;{ĎŐťÚFž@D´Ń5Îw|‡);–ĐJĂ;KŔ¨ˇ!ďYpÍ —Ěj“câŻsěÓV›Đ-2ŚN©űsťŹČt¤Ŕ$„_ý\ĐŚy|Őą‡w˘6¦Ů]ĆŹqáĘá3uŤˇ$lˇÁghŞ˛‘Ł­đcÝuɲ>ˇźopăFX㆑ăAąŃiśŠ7WÇÚ%§,Áď-Ěł}¦úŠq J#㪂÷·{Ó~pYŚÝ‚z/ĐŁFFQI?jWőWlIČ3†Që>DaÍFB ó™›"¸n†,¸nŚF] o/t#˝bčŰXěµF@ÉóźçdśKLő'N· $Žú'Ç6§őÉÍĺŰŚ¦âˇÍĺűŮŽÉË+Ş×2#łţÉ3wŮş¸ž/1ąÓÂ[{É ő: üśĚýőÇUăkF·ŹJjřŞKîÓ'ĺ·±ŚGŞťŘ§ä÷ ­®]ă‡W-k‚Ż|*z¸0ëő[Ô”YŘ ’6Y s®+ĺc”ŮźpJd’¦É˘!Zăµgĺ ńÓ”Zçu'JcZ‡ćÚfÎWnćÔTw,) ʰ`7Ś–‡j{ŤWN“µ}Öű—uéĆ6ű6Mą&W6ÔŤ^ÖmD\:ÉΚökĹ/Í:gLlÜĐܧŇn¶í'ăÖ<%¬Ţ"ŔýŔś`ä­Ű1NűĂ\]*ć7RpjoRśÜ÷ń°€–{j#Ť;ĆęµQÖÝ“uŐhçewO3cJo0ĎÉ,×c|_±±iĎŔ€–;ɦę׌Ň;ÉęWŚŇ×kˇŽ}ľ—ż&5gäăÍQY÷¶L#Ýâş s7ÎŤŢ ±Â\ búJo-čN’%bŽoŠ\ćă‰ýá¸dpďĄHÄc@ÓĘFľ¶1ćľA †ąĹĹI1q˝ŕÜKý”Füsmáä0ŐúJÂŰ š+¸$ä#ŘŢş}ĂHEw˛®hť©ç=­á©Âş2#Ö^˛˝úS´]!¤ě·óçI˛<ĂL6×ĂŁ8Ą°=7$6vb›ü˛ČXdx¨&,°5Wac8ąŇ ‰ |+]ić)ÝčJ†‚ýMČŮE\Ł|űřĽČXŮ´h C-?ĽsöG†iľt îT„kAüĎ®X ţµ°ŔęÜ ńýôt°˘\jR‘ÖöĽĂⓣw‰ztÁۢ?wüýęŽgcHKÓń'ËôŚ“†˘xJeű\˛ć˘‰”J»f˘Mą”(ćË’ZgĘ%‡eű.[ś-ĘĹíq‡ôWG®ý¤şDâ]á!XÇT{.F\-jě˛g¸&cD) CZĂÉoŹ —vŠěŘVŠÓč'3Ŕfs[ńá‘N§ #r%YÝíČo–JîúŰGŻĺs^čôivŢęşßmŮ…<ܤä&* őÝO&*5€áă6Fň´ćµpĂ^SyŢŐ¬`G8Ëńşş‹[FHpőáq7—Őśwpíŕq·çµ0ŠĐęޝŜ<Čőźýťľ•„ĚÂhÚ± ÉZfFÂůĽ$‚¸úÝŁ¤V:ör»uęt? ˙|+ýĚ)ą.wňü ü<áebŇżľůlVUÖe°ďËIúĽüŤ<ű6°Úů¶k]éő2n˝ńŮé2Kýł?ŇňóóŞúś.D™%«(oŰÎGIŤÁ¸ś7äX#–¦!ď*Äĺ(ÂůN‰_h#˛ŐfJëú“_¨…×CzŞžď±/×D ĚÁš«Efęžď±?÷äťaęýú(d஬«… tĘ÷ÚDp "+pg‚źĚ7B” $ý‡+1n:bÄ]ąsů‘H–čît8ę Ĺ37jhĹ[‡„gä`®˙¦Ęđe°sÝŐë—#ŐS‡¤łpgşç˛tďŃKýÉ#ţ-#XŞł2fłöÚdűQéĆŤ—ú;yň?É«˙š_eţŤÖú7:ô?Ńä˙Fo†™K‡/ÝŞŹĚ¶ťź=xżě¤ťx}_”ů˝ÇŢŻZSŢ<‘żZP‰ÝýĘţŠjGüW-ýŹŠúŻzúá_5ő?*ĘżęęT¤ŐÖoćůť U‹č­‚ 3ó»"óŢŁöWżZ‚ž—˙Şü˙Şx˙ŞłßýĽy~W\ŚÚýâýŻJöó_Őř?*îż*Ú*r}Ť(Ұ09z}ýČĽÍĘĐä©ŮY Ń=kçţć!ý®X™™¸»{iŢcMěőކŤçĄäIDpv¸˙ôôšr…%®ĎK;8¸ąč·9„őęÂÎóŃAú&Bşü_¤Ýč˙ mţ‹®ű/Ň ˙˛ç˙E&›˙ŇčżČ ’ÎŻ őm'ęg<ŻEČNóá˧§Vĺ'-IĎŻŘúžKőNžŹ ĎĹÍëk§ň«Ö„·ŻÂćţ›ő{NîoZ”˙KDߎm'śť§Cß™'-ŃoÄźžKŽHž‹Ńßô«ÖoÄ©ßćźd˙Kdß×m'îPť§˙ ˘ý¤%űŤ¸ďątýćąűŤhżjÍřáży˙‚öBäec’ňë&Ź~-ĆÝ{"žW'Ő›HĆć˙?˙Ç(łő_cäÇŤ Ű˙‰ú?ĆŽ˙1üŹŃlçŚT˙c$Üý#ë˙Gţ7ŁŔŤVřߣ‚n;}ŹĘ މőĎçZçű¦çô{T+xOÖ¨ďQ}ŻZ˝żGĹňŰ„ůu˙‚ĺ;cŢë[·ď„ůN`ńŰý&bőXű~ÇĂl7ň®Vż+Z >Y~ó ţ?M—ů§-§ôń»âü'bŰÉç;©çř›Cć· ńÍ•ďDú®“ďIö]—ó@däv˛ô !˙Ît”©ňÉ»Â*žć­Ü˙úÍSŕĄŮűµ ňźm_(˙͡Ęć/dg´ăű­ŮŮ“ćőpţ Hţ&âřđ_`ŢůÍk˙Ę·ýhŕů_ řçd˙Zľý(óĎŹ›ÄS:á÷qWśÂß#’o;…|ʍĐs‰ü=bˇß&ů?#Bt*ü3"˘gá?#’üź||6>žZI'<Ţ"»z·$%wşŃ Ŕ«ĺˇ«užšZHn8i„> ߨ­±żŤSBF“3ačzxL|Đd IĄ!‰DËÜĄ{wÜß;ýoG`óĺ…“Ř#pޱăV|“஼˛SpĆ Üăôô2ę˙b7˙·›ĐCôŰ!¶‰đí&šqţvý_éţąŕô÷㲤ĺ¶č““ďÓéľ!â#ÓĎČCĺňččSŘdÁ鏊觵@mG©Ç‹Î?Ž“ŁO|¦ÓVWYŃOĽoÇăź;/Ä”{-ʬÜĎĐ©˛ËAśw/ń— ž™iŔlµÍůA×řo;N»Čńľ8bţ;ľ Wđ;ňÓ1Öű¦ŇźwąĎďnN$q¤_Dľz‰Ź őČýl}QŤíţÔZ_śÎłľóRsř'a^đ?ab˙ş=rsĹ7Ź>+:ć˙ Ű<6™a}é,˝ü—?Ç‘rţEýOZěčj˙¦+-nŢ{}<˙üO"ŇR˙“ őoÂ{öżő>ćţ /ýżř?a’˙†qĎţë6˙×m¦%:ř˘ÝŹ™#bţrŠba˘3|¸ŕ;ÂâŃNL/YéȉGOYáH†GUŢęÎŰ˙Łôś$— ŞÔ‘"žuAe„ŧ¦Äě1&ËđGöĐbŃXţxU¶ć›Ž d?΢~ě C0)8d› >_Q˙J×.ÖžôQ¦%€z"’›NA_sµp35Mę~ţ'ŤxJ/«Á’§Ü6d»ýł‘đ¬¸Upđ‰ţ˛‡gČŽ ĆÄař#úŢO4JŞ,˘¬śůÍÜ·ŹÚŤç§M®{Öžř-Ł >Ő “Rßő`aL¤#T+Ă2G*_ú ě•s!CAź-wŹX,@<čÂăÖ°a°T‡Á–HĄW‚Ü#덽]öMbb™ Y 5ČQ.CÍ83% E+řÉ_çsśĺĎç7ěŤöŁóEŁqµŐONvńT*CłóEŽ‚:ŢÍčóf¸6¤Ş‘ Č żšhćOČU¸Š yÂş4†ŕB,]łĽ@Růó±Włç/Üč˛ú–ŻéűËŻÂ:0—iʦeM™zW˛ô¶ÁX´Â&ý±ô%ŘĘšPnłÝÜ·Z˘'Ř÷zOĺ2ó%Ó´µ÷ׄŤ3óń—…E 7EÂ_¨„ĎŠ:é󮉷¤3`±ďŞN¤ţůŕ1gÍ>ŐÓŔmşt÷«ó<„GFŹČŢDŕŠ_÷*°zĚеVÎ&7őa,@‚ŻŢPÍIŁŠď| ł™Ë-ţ®p8g qtsnÄ>ăî¬vŞ”ZAŹĺAő5ţŽ›DŁĎť—H[FrWySĘQ×ŔuńĚyoĂ€źA¦‚3™±W"K¦ż>cĐפ°5¨Otb·Ś–Ě~ń:Nëď,™˙â˙›ę"8˝# ńľ‰4ĄĽ0ugf5/‹ay­¤‚ôC\pˇai˝ĺc_×Z BpŢpi -ĄbXŻÜ!ű¨ĺ»ź ¬˝|w‚CŁkCčŻ'ĺySFK*ż¬É˛ĆËŞËs6l»’Sf–”]Ž0˙lN„_’Ŕ¸ŐĄü}伥 Ą?0uÝĆÂA‹řCCpPĂÂ'RsĽR|¤K;ä… ĐęšZö«SqÁńŮď9ď¸b»ĺ7äu "Ú‘Ă5†·ÇתSsl9¬Ť"Ď‘Ă9+rřY–‡ ˙f…°>X±:kÖeg)mµe´ 0úĂzŃĽdI@Új«śŞ§¶6sĺ×Du1 a jdÉŠË>Š⯋&Ľ[ ÎŇ[S%‹mPLÜo¨Ĺšń5]iËő·UPĘŔGđŔ-pf+&mŚ@ŞëŔEđŔ(p]±‘E†IĺŃýÓG׆fĄpU2˝Cm`ýŤśPY<5ÓYEĆbŁ˙×D‡±Üú‡l7 mŽ|]š˘%-~´…Pů‘©ëWŕ¦dƸ7ňşx¦¸7z@KCęVĘ#jUUµI+4Č‹u,G)&7ńđ*óśíŠ|I©j¸ż–Há•6ŚLiČ+iŘ)&iÄYăĂÝÁ­1¤N×ĺ ZEľó;áS8?ę>¸a±áâ>‰+ĐüĺCaPÂ3‡ˇolřćíĚ'¶|f |-mzh2ÉAÍ—łŇÖˇýŮ}É&sŘq›r đ&0QÝĺU\Ž¶ĐťÂeí™hŃzşt“ "‡A´ŇaRăFűŚwúCčŰ‹ä¶+ÂÁÔ]±č@ĺ÷±cb:§ľ".@ŹLŕńŹ+ř ¬d3OB¦8¦^ č®JÇÝoşńŤs©=H7>ˇ­‹Zݦű1űÖôĄZ-w’ŹôXyFUÉ)ŰÝćŇ*Í[z"^ČhQŤ6=«ő`Bd‹7'Ľ†kšr§ľj¤Ôť°tľ°đ7]±F"AôÄĺ7şěě&y—¬KţĄ8zi`ăÍ‹*x›.^}#Ç—ľĎýFÜťkş3vya-÷ă?śUýÚ ™ Ď~™őB‘€ĚhňÄPm ioŐV!ý‹8Ł’˛op'¶ßŮ.\±ÓóC‚iťO ˙M ´ÉśYtÖ´Ü7ž„qśuŢ´¬NrJ{GÄş*@Żŕ]É)Ů ů’şn¸›4ÜőŮžďî39ţQc;f Ç* ‚@ j¸ď>LgDQĆ6Z ŹŠU§Ŕ(7jgŻ–řÎŁĚčVTÄźűŮ”ű·…éăRr3™ęqX9n¤ĺXŤł=U®űÇČÔ8%ů\js ž&ŘŐőŐçbK}śâtŻ´›ŤąÉ±Ůş6…<ËP‹wS_ÝЉ‚Tp¨”ąÓł¤‚ŹÁâ”čŠF…Ľ_~ó^ݤ·2-ĐđěaÁźIQdăřS‰ÓŽČFÄć¤:ܧ ›3űwî;„÷Řńűěű!4ľ4 GĎÓę®Ň§Ăęuע…ľ+îűë˝fCßĆŚç ›µšIŤIˇËµ¤Žű4ľŤ»ľFm‰¶2'»iĽM×}Ă_a,ĂÓúÄcÓ3it—ŤKOîSp\µ3FtĂ\ÁąŻnöéoĺIŕŚ]:şČ÷ś±ŞŃŰëxHZ'înşś5s˘Řž±§•L’lş𢲞ŮęČN$#=c‘řüą™×ëüűž˝cŘçš´Ż;Éݦ„Ű‹^üj“ęľ>ćsĽŚ╚ŁqŽÓ1¶ôlm—*‰řAç0âE±Ű ú“ٵŹ<ÉâAĎXÖ-C7fÉ3˝ő„şeX˙óĎE~/ëňYď„ÇůÁq5'Üť†rěÍ×Üe´4fkŐíŰí>2şĹĹŃÓĄĺúßtÜt˛­ńß«Ţë­Ľ»"'jďś HÓÓ]*60©, ŹŰžŚ)ť‘ßžfKl' #´źĹ.‚»Yb&Ěď Ć ş_yÜÔ«šóěŁ^x& zŁW¤3Ä}ŕ"?řŤC§j¤ÉÓYGYTmu%ű—Ł~tw\~đű†ˇ€Gxv® °¨ľůnŚé_o]GšŃ† űň—SˇÂ|”9Ő$qťE»_O@> ;tśXţD…łâQn•ŁÂ‡Ě|çP+„e×őĂI‡ŘŔNĚYŹčśażgíg+„íríßBO—ő¨«;Ý^ŻäÚúBčWřýśˇŕP¸/Ś&yj#ď'QápÝs;=ľ.‡ޤĄa˙_ĽÍcÚj\čgmŹH‹é­A¸sP0^ĎąMB .ô›XVém©žĚÄÎp–Ć)]^ËÇѧieô¤űRW'łiÝůßçsúÝ…d'ҙ¾ҝ¦Źî¨P¸ď›7j\Şs^Ą?±aaß‹7)UąŘęN:‹máŔ|3B¬#Ú})˝ž{·G-t«uGRZ@«L® ´ÚÔ™Tk˛ CÁdYćK,fŞřVĺ)…}÷WÔ·«Tm4É”}…ýĎ[Š_°¨(Ę%ç×umwv\^Ś®ř–—Ż=ć |ŰËÖí7ßă>»Şöüi˙:ë:Űu–ßŕa^`? ňnpěđ˛´q\‹ŔĂÁ4ĘĽ“ô{Y°9Ŕŕbo¤żf¸=æwkĎmPëlwaśqťŻ?đlßâ%SŻ·î†ăă¦Ŕ¬ŻŢó¸ŢâĄx¬ŻŢ~!Ć+.ÖďąÍnđ’µÖ[ˇđq“ľäĄř/]kËc×·ś˘•.&bđ-Ďk©g‡±şŕ~`d˘Xmz$îéĘ®‰v“}ź‹a?ŰÜ`´>‡¶ŘܰĎÄ&ĆuŚyĐ~š(ň5×)č ĐňjM춚٠<Ź1ŮZX,®%Ĺ’Y_¤!… áVÝ‘0¸Ń›|g0ٚν[ÄŹ… ěřEËG őáŢÚ7?¸×›wy')î+¸YŘq‘d‘tçë8M/^u8x§‚ é)OËJ úžöˇţ{Ş:·îQ¸}*‰ŞéÎü¤4l8@2˛^2LÜGRV^GGŠEóEşµ)‰¸éÎ-„2tŘ—Óë(%ÔéCĘVšŢĄĘą¤Ż;©đeľ/Ż;˛sN2xŘg˛î4HÜĘ|E¸Ź¸Ű˙žépwZtUÔ-y0Že¶ TŠ”%ęAÝ Ő˘t’Äwlq:“vI˝ŞôŇ©ÁąJ[Ö+Ýb-ӨʶnQe“T)É:«SŞÁŮfÓrYUhéR@%Ľß÷=€\č7‚^W´+!&6›˛NŞ:“łMĚ\żă˝˘ďşLźŐôÎĽ6„~©I¤{č]caO|’‚Ú2¤„^v Ěh3÷VŰ3JQśĆäĘO ĘÓN97+Dá°wĂ÷DőGéŔÖ±—Ę™ŮóÝÚç—Jx°.šłËRľa„ĄČw űm›üĄ”÷^?łŕt“÷\Ŕ<×PjÓwV×ŐH÷đ‰şŹU’'GĐłŤÎz81íXÝ@őO×p·ąĽą“w–ĺő÷HW-Ů4HGçO…đŚn”ŁÂ§¬wĺjÎňOCfd‹_§^Ňü)˛Чx…ĐۇUjĺ^5Rś`ŠÁÖę:Š=L9î˛ă ‘/W‚–±)q™/ńlä%‚’Đĺđe¨ |źŞ}Ş„ čőĽÖ‘O{·:V¦đGÁ±îAáľď¤˝:—b*@ě$ Šw®BűV ťsŤO©H/™+Á± 3:××ÇJôífl'gp!l&†Î(„&0ôNwňU!#¤îŞ‚±ľR·„–ÂkŘy†ÔŔ-˛LFĐűZIdÇs»‰Y?HďuW_ÖQŕŞÁˇŇ=Śč_om $X0Ż· — 7ÜÁ]ŐxsEyâţ:Ľ4ÝŤ‡ČŽóŹg$cŔe/ěëöÜ8îľ´ÇGĂ·AÔľ•ÇĺͦYęŘ…ąn3|ĂlâŹcŇ@÷;-d0Šá—äxŃÍ.YForNw¦ßR*9 ŢLTÉ+7 D„]úŢ˝/-.\‡O¦É=.ĽV‘u–«>'Á*řpćĹÉ1Ş Îźśěú4˛Ş_¤)fą­R4˝ü”ZÉöÇE°–­Â?đöłc/Pűós)fµ|´8Jhßßă•vŞŃl§d˝ Ŕř¸Ĺ\N/a ż®62Y˝ohä•/3Ŕ]÷˝q§â¬@Ožq.;Đ=Ď» [Ú‡ŐѶtj„@±p·˛‰I‚±pŹ“3Zk~ď‡'–É܇Ťň4Z<Żźš›§I|ĆŻ™›ÓQ]FŹ™—4¶'Ô׺p{ϲgŕš€—4ת›]$[oÚ#”UśPš@—…ČY˝">öŐ’DC‡ľńçvIj#”XýňrA^4*!”űk-ŻĘX©P«úÜZ4~Ó›‡ţ Śl‰ŔÝfrvl÷ňç›TF¦¬ű[ŹűĹvťÂéÝ4>öĹm‰>vÓď·‹gxlh™űľ©ËS†6Ľ‰!O2]ĆrCˇŁĎt×iśŔł‡ąÄ?Ňcw CI틹>Ĺ“ž2ńuÖ"†?‰ §+ťř©ĚűÇç—~úß›ď'q×đóSžď”† Ąö‘Äĺ;ŘZđ1öźĎ»·=Lî3*ë.L÷‹3¬€G B1L*“SúKČ O'ţÝ1ÍőR¶ňË—°˘vÔ0,D,îbĐ .BĐ(Xާ˛ĄK”Ńëîjá ÓY >¬‚G ^·ťžçěOŤĺ¶›–µe+Ü%ňšíç#J§#hIŁ™PŔuµlŤël®Q‡\`éś^CC3…»¸ZVDëŤ ¬D–Ś×Żce{~»g;«ýµÂTýH3@ޤ>>Ř÷ä…şóŮ)yP/Ö&Ö’”'=ڞ(‰6'ţŢ -§ůŮŽęŻ"ŤV[Aâż=?ŰĎeČŔ‚łą‰·!‘ęß{H×–®\Ő/OĐĆ“ř„ýy%cÖĎŘÄí¤@Z;®$@‰ş­˝ÂÓSĺp—ňWBĹýš| oÓ$łoëoý@~ÔG+ n5Ü1 obŢ)oµkŕÝŐG#,6p¨ÝÓÝÉÔ"fŻ»î Ç_%&wO—#sŢlvk±q—ýń'WQ´řńżž]Ĺ^ÍO‰ˇ§ůş”X:„hři›*Pru8ź‚Ą¤/µťOŐŘV !ĎúŢž{\\MŽk›Ľ6ţ8¸4žOćśT·Ë+ĐXŔ[„\ę=^•ý‰7]]6ůŘÍ=TrGXZoÄŠ.Á0›Ôtňô˘´KWíCÍGwžUěti›‘FU}=ÁĺŰd”f ¸?žÚşĐXU~[Îΰ|`ŕ¨ö҆fúJż8óË MEÂ÷,–âŤĆWĹQÎrĺš›9·=YRvji ů;ɢňY[ööŕĄ6÷°H㯤lV„7k~äĆŔ@†‚Ă?‚č·#„vÄ@řčzj¦źv6řľ)ˇF Ü›•ô,)3‰ö»Óř áĎ‚+ŚÍ2o3ü»:5‹Ň voxÂ`c¤B…‰*R)TĆc"…ŢîuC§ýVfĄ˛ÖňWŰoş#ŃŔN'šŚ‹Ęá§Ďx•ÍX4öB ňŇnxZ;ąMŻZJüŚ˙Ëć° ŁNŃ»‡€.Ą^Ń«/tC@ /‡Şi®{Đă]®,ĂĘşŮGüŇşő–¨ŽAU2ýFMdd"ˇSȵڇ•O¸űő‚»a`ýŚXg°*ĄĆŃc+Đ:ü¬˙_ݎ/ôĎ'ăo´X…oyXö¶X*ÄŤUOÔ-‚ĹH&DNÜŽ@3†%h€Ď)¬âS_ÚPn8KŻ@§4Řp¦DăîÎúUInx—3—§ś§˙6pŔ_N†hh'¤©!]ŻkżdAżdéęŻÍ~Łţ8‚ţkc<˛–$0:ÄYĺeŮ»ŮPŽ:*~ÎÍÁşŚżŹś©ľżś!#\AĄČ€A¬ńůţľ˙KŐ·¸ö±w—ěKYŢŃ:XŇÚTńË `MÜĺ$\Bµ&lµűšőĘY¶O}ąĆsĐ ÄrąĆuđQ¨*™ÚÜÉ]ÖÜpt‡ľZąˇlµş5'á¶#źĂ“ŘgÜB­qwđ;{áü)8¨ľĆp=b}žÎžíąĆnpqËh lýúöű/ž Ož„¬Śafňŕ´šď5mŮîŞ|u€ľÉŠÓ™Đ«ţ,ĐůúÝý˟Ů9—]¨wÜ]ĆŻ«/öUę˝á«üTe+čFTŤżçfđ?®věď„w|ýr‹l§wĘ6(Řkňék2 #¨sτޯˇ˛wi ĆPużęę3ţ"ŕů`çIéxÚ9|"&¨Š§Ë{•ŐŔl˝xIĢI #ŘŔűŢ’“÷dřĄô›ňďoôʵ>%mž$źÁYný='÷Ş”[¦¬Ąż.™@çźëâčXę»Uřnن)kO‹č«ăřwúÝ„LClpşÝ†'â`8~'ˇ* T׋˝ńe·Ń&2ý/©2ÚTůqN{Ź@¶˘5ŕ»O”Ľë51ëÍ"h=ö¤˙,f$`3i“LwŁ ~7&:*Ę€äs9`†~řD·-D˘Í«éW)-cb(eŤďd—Ś~Đv%=<Śô¸čX­źŁ:«dŘFzĺ?4śHŻŤ%ü„ş·WHŻ#ü´¶»—áÝüpCÝă„Cö(ÝĄ\­IŽŇXC@ Ę ÚăQ±-*ă\‰±]+z@€vůFčîn{ł„˝8ďÖ ž©šĆEĐą‰.ú´łW|ťű¬O×sH6 =VĎ:ŠŔĘćb€zyKčţnsÓĂĹŢ|Żi¸YĄH |w#χ|\ďě'‹!Ł ńwVôݶä_ ň=ô©ˇ€ŕU>B0DB'ßß|ř?rŇ^YńŁ ŕ7ŔŹÝ·z˝cYçÝĘÂí–ďŃČősžćÝpľ‚뼦¶±NÎŻ~€ăÝĄşW ™u\„_t‹Îö/nŃmîŐî;äĆă pnî…ćΙ[[9ÜHw¤4‚äVŐ ZěS¬ąůśRg¤ýĆi—0):­DŔôy)Č-vŘzu‰˝äH™ íHa}ÎÎ%üĂűçŽ#› «$bSťk~š]@0±Óá.Ó/@nŇi¨¤üř+;{Ł%˘€µĂ‘ŐôČDž‘Ç–ŐôĤDúč9­Î EˇÎEf…ă…V@pG¶<=¶"˘ %CŃŕ± Ś¤0z{ä¬(f‚Ý ~a$3Šîť°L¦‰*EçQTĎŇ}šŽÝŁ@Żř.ÇÉîEłW~[q˙Î? ż&ôcäXň €f™0«ň éPbó(×^;šJ»0W˝ÁÂd)ĂLˇve+ üňSݰ~‚¤^á(´f˘ŠW»s‘Ů4SĄ¨9zLëřęYúÝă2Aµ´QŕôpNóNűž2w.űÚţĺWQĄ WŃ‚ćëMB䓨©ŚiĽ'ďŐî}Âú飬Éj.–nµ/§Ňá]ßß‘ńLŽÂMDű¦6!ťĂ»VŔ5pŠn9peűd)žĽöźŔţqžCS’–QĎWďÍDz´7N`”ÝŮůflńlwA2˛ ]Ă€G逇µ`żŇó˙ŢĘe—©C>IăR )Ëúže׉`rúb.KyWĘą3őÉh~DZT —!Ů– 5Fžę‘jGZ 1$:YE§7´‹ćęĺ˛;®\˙8Ţ۲ś­@ Ü榍’ůŮŃÄrîćÍôǤ>¶­ ćÎăBôĆ˙Š7fH î] ű†ČEhęf‰Á€ëúKżš‹ÜCŕoa䍞žxĂDX±1nl‚ ÇiŞ]ő$o¨ĺü–ĺőµZĽ1"~4+ËxĂDú.®Ń ¸§öŽ“_jăÇŻ*_ęC׿*Üԡб› ŐĚÚAOâČ {v·ŃÔÂ˙¨˙އÚÄ:2Óńnۢ3AS_ňř[Ćr¸íË·óoȱúHřěă˙1»ěŤ7ě"čIڇóÂuC(éîptŽ7| áŃqOÄ.{t›ţŻňř=ô!ŘçJ(°‡çÝkĐߨC2 çÜ$ŤÚĘ#¶tŹŚ;ř'¸ŕ€ŤŮe4ßČÓŐ9“¤ÉYŘ=péÂÚJDŰîV Tv|`·łî„ŃëO7áżV˛s[^qöÂ-źÇg©rCţ«LáGĽé‹Y^eýź3±ŔuYžE~o´ëąf‹öXz,Ősáţ(Ď„¨eş‚çłÁµś‹Pžś%‚ľďšmd_2ĆWc|J:ťI¤Ď+ö(eod±ţQVH$Cͱ.z‚÷ô/Ä+±>aG0 ýi˘ś^Ż8˛O.2zRvN )đ˘dŚwgg7UÉqj¦Żş•ý’úbÇ0ÝŔqúşó)"p D+Ɇěuá+xĎơlچ{”ýŽe&Nu5ő:„–"ł…™É/ćqFIĽˇKgŔsÍîšÍËT1QĐQk—Ďuäv¨c«•{ˉűSKvŠcóľűH=Ŕqú9lpŃí.dâŇŐ;Š™±ó0ŔÁT(ńč­N”Ž'őV´c—7sřů7m”Ť;âˇ,ě`ˇőÇĐî) &ëádÍbîłý¦-D`ĹČ3ĺ&ŰOţ;Ĺ 7›ş~Î}÷ÍDĽIĽňC0mümO86uçěP;Ď=,0˛ ¤·‰B í·ŽÇţÝÝť7Ď+”lô€žć{ýgő]0uČĂÉ}jm|-B›‚1´a˙%ŕ4‚ŰůkŁ*ĺ´ Ô´ÔĘÉţđŞŞĚ©HjűŰű«q›ë8ŇŢö·ţW2×'NŐööżřĎŮ O–†•Qe9 F…=O¸¬× $’o$ä(PŽŻiV^ÔőÝFlKĄ7ĂScOÄ5› !;A˘BË0-B>q€oX#€Ň0-‚uv"DS–ďCřväł+]îśdn«“hŹY÷ëőHsśš/g!—)9˙ íă*śˇüŕ%ůˇůˇ đź˙Ăă`ghHĎ@űÇĘô·µ!í# S{šß6Lڬ4Ć®˙KjĐ}&¦oIĎĘL÷ŹN˙Żţ}éčéčX陿ě_QôŚřt˙KŞ˙ż9ěôěđńl쬭¬M˙ÇÚŮ˙żŕüg˙‘˙rđBÁÁđ`Xýďn=@¸Š¤5–˛ö®Ž-‰"Dé=‘#’ăân5÷łQRłŻË›‚ţp˝ďůĎ*¨üpDfÓS[ÄźůwŃ:ýn׫á"Ďą7ť6Ç0Ę»)°°ęeĘ—uÓIO-\Ą*ĚÂóK­”ŐăÍÍëikÓŐŤ ±6`í…=Mă-•8ćÇG釋:őćŤr×ËÜĘÔM«Dě•NÎIW‹ęOâ­IEjµ­/ć0uޱV©]Mú×DĘWÖ®*L"†ŻÇ$Śú~ľ5śQKę׻ؗ^üá2"0wKŹüÍ™Şíkié¤ZΖ±N6ÚU°XˇÜŔŐăŮ~Î\‚«°Şä\N3X'Ň:»RAxő@V-iÁiłęX8sâ~Ő(t˝tsďX*1Ó˛hU›Źşz±¶teĹ2x5NÝe~‡J˛{­4đ±ľî5´ŹbÓ<%ĺËŽŞ2§Ę»CĚýš·źCŞxǤĹIµ‰fLđ‘Ú§–Ny»#•ŇW8)hűkżA7˘ś>Ăż'm@˛ Şź<(3@_Ř©6› vy7[-ôp®.CŠ0‹ ľĄĄbF#=RöHś‘GÁR'YČĆťZnÚBXĄ[ÍwËÁŹv¨¬kâúG\“ĚB˝Ź·Ęń MűÁüô#4˘ËKʆRą˛°Q’@X÷+{›’‹xţU˝ą€R8O » g™·yUŤ@Ş™ G¨ĐĂt­^oWڎYµ_ąm.ţoů­µk« ąywbşę€žóg`ęĆQĐÝËňÖ™d-¶XĆuŞţBmmČ–‚ľó? ˝ŢÇép8ň(ôScżBÚcS|˙Ö¬E/ u­Ĺ&14ÚOtąęĂHW?ň©Ób!1Ěýľä6řAęä~ää"• ‘5XTgcé+Á|™H]ÝđC»qŠ .čŠęá|ס|¨ĚŘc+]3ňGÁΔSPE ¡ĘńţtáI‘UżaŮçÚ`«Lrˇmľňě°0b꾯Ş´=Î4ÁŤ UiM¤č®ä¨âc”™VŔ˛iĚŤ  C1s#rÖn5@ Ç'*­Ëžű2—đ} 킥´yJ&˙S?IÂĚuăZ۰YLľŽŕ(oߏĘX?ř1íˇ#'’ «!/†ř%ëÍ(ôź„ú»{ʤÁ5*…±ttxâ)„vÇĽďĽâÁ#"ęF»Ř M°?ť!é’ZęŮłĽ2| řqúsRßcUŮÇgc|qN?‘ů“S0—ë×sŹx’x RK°`.ÜhÉ«Č=‘4,Ďígáů 7.ž±e~B»4ńŵ…ŚÚ8ĺbünŮ˙8ďĐâ{D§.ĎŽDĹ jĹSëD<ćJę¤b´WutżyĂ3všÇ2ťëżś0_äňM?ďL)wŁşY»š<Ŕ@?̢úŘe ”Ź(¨çĐze“ťx]Ţe2;Ărw{O¬uňCWf ˝µŘp ­#GŰ˝uYe1S_xrhhŤĺ&äJNO†MÔ y˝ÍI5Ţ×ŐÖEú0ÜŁxłŚ6 ˇňĂ.^ łU¤G’y48¸9˝śkZc ´_ŘČ:UÉn Ě âÂYp8L‚„íVş“ľd©€Ş•JŹ>}ΖËôť5“"ť¬ßŢJHĹ—Ř®ŕ‚+·-żVLčBëT­RúăńưӢ´Ü`­ .ß”OđËCŐ;Qâﶦg'§pb>‚ŹŕôlqČŃ$2ÂŮűٞ?ľÄÂ몟O+ť(çŮŽE {)xćB*™Ž±VřYďęR Qő\'Ą‚ÚÍöθZ»crWŁÁgôăyiĹěolű/°OVIMcŘŹŠ›C;<Źěłŕł¤äÂCbR·Ďř9|›XŠ'|ď¶X›sSvŃ TR`9Ż+.›0$cqäćn©ÔâĚđsÇ+)Ǹ]!|yqčßŮPł`¸ČT Š pľ ŕµLbři@~˝÷1ÇAżv!ąÚÜD?Rä’ć’4“Xfü“׍§ęĘV™®3]Qđ.7¶×Ięr—RHr.\Iiě?€Ĺ$ «ßT|Ö Â"ü˙úöź˙Sç˙ŕýĎŢÄÚÎá˙;ď Ś, ˙ű÷?†/+Ý˙ýţ÷˙Ťó˙üţ÷Ď­O7µÇL(ŕŐů„”j5Č0]߆”o4.(GöFäčI;6µŹ7Ź®ŮżČ¨R%ÎňŘ|ôĐ)ç ě ˛N|ő7đšĺ=ć¸äG‘+âR«!r® v·/fÍăPcö ·XĆÄ\gĂĹ|±20Y_©fÎ.ß ÷óD×ଳu&®ÓÓťMš¸ľŽ¦Ne‰€ÉuŢUŃ l€ «\ęEmÝYZ ö‚ÝhôşżĎ,ŠŻĎ®˙ťý çń»¨\q»Äb>Pđľ<űĄAî}DN°ť»!ői6sÄăm6ˇhWCĐĽŃâmµŢ ×ü¶3Z÷}(:e7Ż!˘B§Ő’""|éSĄ¸ŮvVEÇKIgSoǫƔH ćöłű^EQ$1şV~X.1ú8ŻFäő¶łÚ«ĺ¶8­1ořăÎü•,ůŇŚ•1» ErfpüAÔËüSH!Řď×sS˙Çď Ż)#},űămŐű˛WŤWM+±9qîť^é{·WÜl±M¬Ä ýËk,Ç+™žăž€)ŃoV$ÖZĺ„ĺ•LŻßźeŞk*lşhohf4©ŚŚĘ¨ąxu^ź•LB<§°FC[—g™b%ĄŁYpç:ĘKĽ×G*ů™Q±yVł7<ó!>j¤˛·#2CßÜöŽ'¤ĺ´!Öd?%´đ!űJ–@ăÖŇöghÇ Ü¨sŔáŮ+Ţx‚tŚ‘Ŕw`CŠk®Ćš™~Fy‚F2‰˝ő~Ü–1y†|Eśź>A^—„`] é·äMm2i± ĽC”Ź)ą~^Öp¨dą .µ¸^´ĎÁźřQeů)ž)÷=bŰ•/A"śÜ'™wDr=MIî*Îď^–€\ÇXŹç“ůĺëĚ;"ąľîLç0§˝ Ă0›DŹ–AJ”°yĂcßpdP«5ë´  ř^xW ţ”,ťşŃ°˙34őî č•cĄĐž l-Ż÷cKăŚT$Rm:Ŕtl-tČb˛ľ@O1q6ľ€O>q6ľO9qÖľŕ÷Wâď5¦ĺܻȦyóąý:?ÓśN…ÄYřSíRšBi|© ÷JšBę/Yöe…'µ^(5}N…9±SÓ»1’Ň’‘ä•Ú°řŞŇcťď˛™GSh¸)qŰ©¦%ÚŕĄůĺ˙–ˇ-i|VůĎ›%4dę¦^r‰łúÖ_zMaţ†ÄígnKŞ+ĺř§ě÷ śésu~żnHă+đ«]ÄŻŔOcă`Ń›d!ťyËüĺO´•Š1©‰©‹®Ńęř‚wťőűóRŃÚŹO“Ň:Ä_3pSLžMb¬>s!ŽśřGľ €)Ýp‰GV yVcEî8Y.o6ÉŽa%YöŤ.{QZXón†‹^éĄWK‰'ŚĎf¦<šD4rŹA‰€Dń*î˘Ä˙¬d6bů HŹbů˘‡ć˛?Ű ŮĆéŤtŚ÷j€kÍxě{Tjz“n÷ZBBť…ôTČQ ŐÇ4d*3˝¦Hf ¦„Â9ŤÄV¦×Ä<¤őźpmfT=¶Ś}čč<ńđŮßmÓ”•߉ÝŢnă꫺iłźäOšęó¬ fŕÔţ aˇ¬–»úku(Bě›e ?űU1W×Ë6 «?źkQ°Î06÷ű„C–Ě[l@‚Sď ˙ŕTÜRŤ·ŻŽ;źŤ6fRÉňĆ7©Ţ‰a7yéŕś=ĽGb)ó±$©mJ4·Át9Ą»í4 'ÁŁ({’ÎśÖ\¤/ú żŕzXäÂt”Ţş2}ţ*‰5(ŘM°”23h™¬ ÖíťO× /‡ł€›;ŻWłĹÁ–˙1š_ęŚÓü‚cˇČ7éđqŞ‚ň˙Ń®YuĹŐ5 w'8ŤK !Xp‡ŕ. îîî‚;/î @CĐŕNp‡ŕî,Č4yç[kÖš0sÁsU]§ŞÎ>µ÷®Ş‹&Ď÷|ľýBhćU©“iß[HG,t‚¤l¬(ŁŕăłL”[ŐŻňí®Y7‡†-IîŽXZ‘z‚uţŰQI$Mˇl"{ĂšîZčŻBDśĐď$`źT=mę~űШ$ =ݨ¸ĺ—‰¤ÜR…;LN;­7‡|« ™ GÝČ+Ü ?Śí…Ş÷††»í„¶:…ÇÚ#Ç„S/ű`čŕŻÜj˘­gŽý•śDŹ˙ uÖÓť:bë˙Q`Ďă‘á·ĚtŚpwî“EŔâćë7a|ĺ^€@¸0Yîu#ň“ŇřSşŇÖW—@·‘ň?{¬ÂDăT‚Ü=PP ç M˛a@®Ű/ŘCsi˝¨? ŚdÂPĽçň4PţzÖ}jé&ĚŃPOX?ű˘ĺ…~ÍöJöqŁEĹűÁ5ężQTŇ–ą§­ś%şGâi˙:ăŐJ }  Ć#Ĺ‚şU6,ţĆVöS¨¸±9¦†zlV†F đçáśµ;¤îx(vüęHµ:iĄ›*zk[9{ČNŃ^PąćĽŠ‡‘Ę ‹¸tÖr_ŁřóCăŔÓč—[©,x¦âđ¨Ü¨ĽJ'„&GSgń-ůSă`đŰ‹Ą<Ǣ>†ľ ÷ľLăđÜäÍGá) Dôüëz˛çŽTRŻŻ63)‚()ţS [vówEáˇw<¤5Xj˘Ĺ"ÚËwÚ  sĆWW+~ç>š`žÔZCí—€jöä>¸Ś{NĄűbůI’ńhŤ3÷ýbëM°†ÚŘ .9*GY{đ!?‘˙|‰ş¸`ţQ—ópÜé7ÓşÜăľÜ⬠׸ď9˝€mÇÜó”ćŐŽúôÚcůíĎÂ.y4yTčĆZv˛ń°–ÖxV_ń%? ´{Ü·Šč’i?ŠĐt™ĂŰăµ-Žö†÷1ěެBÖoŠÖxh쨉:yŠ>zg]()Hí¨U±&:¦_>/!Ö{E]ć©-»€H·ű¨ ÍFť:–FiŤWĐŔucuĄvníź30–†/fo ő¨óßćOYa|rV™©„^’ÖĘa¦ĹBŰEH™‹ř’‹Zź=‚ŹőFĎô€ăŻIˇFçz7‘á“Đž’I€íŰŰv’ń†:c*úrĄ%püŇÍËvřĺC3¨!.îâʼnĽŻ’Ýů%Ť@űńOé– xgO<ąä7Ó“¸¶xlÝWz˝ ź§ż&`vˇWÇ<+ ĂšVPÖžż•ç®<ŢéŃRjUĎ!·bX4Ýű„ú|›$±=$ÔčZ ËôEüd\äľ8 `ĆŇE —Ý;çŹŘVJO„SÓ† Gx» Qˇţ#3_ňI”Ć·ü(wôȬ4*ĺ´ş˘P€”ĆŔ׫T—:&ÉáůŠ©ĂÚťZňbşdW«hÔő0…ĘĆx»Dµĺ<ň&Ç{7Ü'&ýńţ4Ŕü‰ 7i`˝4ľ‡ÍYůůw~-»Ľ©ŹU ńη'9>)ą¸R䡷Óŕ¶ź¤3ţEŰ–ůć1ľ–˘Ň’@wvÇŇçÚę.«r8ú'ĺöQŤ őŘôD˙\íďt¶ţ%OĘŐUj`˘ŃĚ'ضfNKëLŢË0/f˙ä…€ž¸ô:˙\Íďtł=w,?u*ŇÉPőSÖ+#âŇż>ň‘x+2Qú…«ăî×f~ĐÄ ĄT0‘”Ĺ=/«)®B@rżáŮŠŃ™B¬݉ŞB4a™ ö‰O'ą—:ŠÁn›Ť×‰IEĎ8Ę=ŽOq.cĚ.‡Ł.Pq¸3t!%ĺÔOe‰m­Ęńµą¬,¬ŢIßY®ĽÉ‹ “Ňęş,^”ýRů9|n"°®š‰çŃ—Y¶ĺ'‡ú´MýĚÔžxZ¶Ë{ÔËěV[†}ŕl°˙ÍŹ9Í(Oď ˙QCUŃ×zIţg˙ňŮäH-ľ  łđ‡"n˛·ľÄ\óŞ[‡OŠJÔĂjKáKł§ôçfPŁÖŃŤ€G?”ęŢfG;j»÷í9’Űk3á-§.Žř˛ÔNPţÂa÷É݉+Ó’$ĂGz~¶“f?fď?đť(4X[°Ťž$ZĄíM–¦µ>Ť(†»:ŐűŽ˙ţ©(ëÜ®ű‰_°u>‡9ö‹efČlć~°KTA´ «µP‡ř‹xeoÜ›Şě€iśpâZ®go®ţI–brÜĐ^߲p„-Çqvŕ4%»¨g+[[šľ6kˇź.Ť,:ď(3ŻOFZ˛€K,& wĄ(›ŻÓĘk[âr‡••˘ž%ɇNTY¶Ŕ‘Ŕdč"A `NűI3ű=ß|Lćš÷­…źlă°ô~áoŚóy«ŕ—±e‚ĚYdţ‘ Ęu–Ŕ1­$ďôV®Óě˝cŰC9—ť>Ş0• pÖ%ő%íFäć¦Q –XŰ)žWŧRv–kË ŘŘ!Ú+ĚÚký\ąËŻěSĂT˙˛'‹l#Ş6üą˘vĚϡ7ţOËté/łs&ĂëN[łR%\3ňňí§Ç™ďŹëőŐ R˙ Ócsµ÷[©xv{Ô)M›ovB„*óĄnµŘs­0·ś ÓmfLL^3ýđŮD¸µ§âž%€C†/ŠĄČ1°ľoáŕLoáČş~`aď á¤i‰t…<Ă·BĂ ±{ظîtĹĆ1<ßlµ/ă4úçc~Ўww‹‘3˙4ĘĘňYŕü˛b}!¬ý*/ ©ZĆRQJ3Ő†…ÝwVËyŘsůe˛ă. "•÷ß5llĂ)wý@r7ÝÖz΂5ľÝ¬˙ŞjB´[ą;“ť[6ߨÖëđ(˛¶;@ĺ‚Rbł©ĘÍň9• ’úěÜËňˢ’绾sŻž Őe“ě^ü{:]Šá˝í–tˇt‹&lo8u6Őď$J$˝"PΨ»>Ó]* Éąë‡F|'6ŰĹžü9Ýç~!g‚ŕ¬B¦.Řl^¶łbÝŹ™Iî2ŤăµHýżšż»´ÝD*ëÚmÎ÷1ç}®YWv9î¸?Öž«"ą e4Î5X,Ú-N]‚|9Tçv-íĂŻLż~芼=ݡ)x4ô+‘Ä×5 =¨ĄŢ " ëăưŃýň=ëźřBírj,’ż›B%Cącx“÷¬vÝ­·ů9B“ÁQ&ěe0şëdżöîŰĆűŘŰ5ŐĘÚƸÇĚŽc4Ô]e’-‡ÓźAjMc(3ŰsŃ_]˙Ěp´+©™âU…hL<K¦ ďd˘ß ňiI_Şëľ#ŃTTVŢ•X@˙<ťS‚ Ě:oÉË­W…jý6o1µx@–*?ÉÇ©ŠP9©m}źr%-âP Č'XaçĂ8kÉ:αý\ąŢęîŘqĐóĆÍ D",ÁŞřdt4˝ ߀ȚŚnu!L@ L ––‡V?Ň ZöÄŔÜ Čň§¬Ż_LŔ©c9S 4°‘»śě‹óqRBţ˘˛‰®Ň”9íĄ‡âě"î2ôĂ hăşł•2.({U{עŞĐMÍBüŇ[U–&. ’d·®Çnźk ›Huá,¨nĽ!gšî őŔÎŇ[#°iiuÇß €aőŇÝ…¤>ť2)ţDĐ_“ęľ‚L±Îŕ,{ÔEşóOó([ÁŮgEI‚é“»°™ÚEá¶é(““;©|o2ś¦]ň/TG›ŮţT‘íât7j*!ż0š–.Ç>`_ómYŹ H´NQĘy?…| Dy¤»‘">»VťVżćŮ2kÇIž_Ź |Özcč/ľóűäÓ‡śŞÝŐ!Ş.LĎŮËÓŘÇ6ŐŮţ,Vߪ) ĽÖ š¸gâ$Úľ‰ăOd‡.ď‡dĺ#ń;¸EPiˇ$éÍ3ßH¶©\‡S˝ř˘7ÄDć'şČ#đõĆRbzć„\I«3Ë[¨·†QnzŞr< a„şě†0¨hŻ´Çíµ*ǵT-¶Ĺm·*'1ÓˇS6|Ó?Áw–b&Ť“p©Óăˇa‘ĘÇK@ ŰŽţŕŮ*bvŃ…í™x!U*­®ÇĂH˝'¨Eˇćąž&đîąyµ[𣅻çzđ‘ůqĚţ0*}/  …µçzŇŰ Xř‹dĹ/ŚBŠJžëQf ¸Ňźôx€öů‘0»°n˝˘0e¨ďŐBń˘ĺ×C†[ěb\ Ž"CKײß1Z~7şÎϫıü†ęn"˘C[ËO@f­uümoÁŚă‚Ź`«ĄžöAÉ•>ŕ©1yX ő‚ÓÝVsA’;ľý)źţŚ#~‡kĺ‰qU†đőáPôŃń ąâöT“Ŕöç¦wW;Ů$<Ó 1ł´–vˇŢ†™wßQĄ¨Ä[őbó.ri츞‚Ĺöü[ő{3׉ź?ŕ›,ľHůl«´¸N—”}8ŇsiÇŮ5xŚhÎ_)3ز~é÷-z”Ě 9Ě×59ηt‘×îŰŃ·Ś‘ł¬ţ(° <–“(Kz«/p ܦďÉB°ˇn&»}ż­ş„?v/‹+™A¶7 öPÜËű"V/P¤-p:•$gyǢ‡Ą%˝3H)ˇÝşѰť§ú}ňŽĘrÄ18aůÓiřĎ^ď!AÝ«Ë<¨Őzč@iöüŹňŞžłJ©H§ëłßvîZhu+żÄe@RIoƏɨô^< m?Ô źµµ†vű Ž(z)ą7‘„DYłďž%ÎXła™˛Ęި?Ö˛*nČ0†púS°Du˙ž,Šě@Oiľ/sÍ,O}÷gČ’—ßŇj˘tÝ$2رUŻ9˘`;2{Çfż\iŐúŠqűýą9 áôÍ­6—_%§›C;–^©vo3JŚţY-Ęř±áơXôľMî»Oo˘üx×™5ÉĚsżAV˘(m„˙i’‡6°ĺÇ®r’c\îćMbĸ+őäŹ,˙pŻČöçęůícŐĚÄT2F„ľŢŽ˝řjž ĂM›Ö«yÍE{¬[ĂĚČÔěŰČşâ#Š‹i}R–Ô ó''jŮ"е¦ŤZĺHd˙őv¦Ź§“ö‘Žo.'1QőßěÜ%WżÇßu"ĹIő/KÚŤŇ©–qobŤ ć÷ńĐŤóűýr"m˛6¶ ęČjĽ;˝N^äFa¤ĄN˝ą•mI{—ůrs™Č`ĂÉsÁü|+Ą1R«tf¤¦ŇO7ďŤěp€Ýş´łŐ+Í‹ęë@ Ǭ6|ÎR¨ćMV7Q“©jŰěZśĂ˝˛źJâň‚ÍRnćŔ2đ`­Ěé÷kĚSŰAś•*Đfż•g„%Y‰˛¬a˛Y.ch›ľ%ŰaśţS &ÎőäÄmw!ëÉowĎ;«¦ŢŤIUŰ«@a3Ž©ńpáĄ+Oňx«ť FŘ#94Ůű/Sţą'-ńöŇóPëő¬ëX\bŤjĚ` TCÜŕ–ŕ*t ‘y´u»úń÷2bó0ëd˘áUy¨âß5+ĺDżcwi Ůí§r=Ô*}BWĚÖ€”™Ę¨†3^ CŞĚÁ Ă…r7¤’ŕŞÁňß6 ¸®@-¤+¤/Xşâćbßő+^l00KCÚŤXúć;ę-J#3˝ëưÔîŇX;2Hä$„MÝăT”>6[1Ňş$ qýĽŕ†xWÇŰŹÄŕiN—¨eGŃT*Ŕ‹‘"R—±Ń‚ÔÎyˇę<÷·Ř‘M†z,;ňôţ)+JŹĺ!öXóŕ*Kî3Âôţş˙¨vá(›Cc<Şg?÷ŁQ_z2ĂĹ’Ě÷Kîśç«¦Óo×60ˇ†ő”QŢÉ NŚ–ß2µE’ UO<3G¶@˘2ň~-đf\ěIs4´^aqe­ďÔ°şF%ű.BsŹzY‡ýŘSą“'ŃIÍůÓ‡µ­(!wúđŇ_#}_lî}ň¦€äÎE•Ň»ţŮť‹lŠsăl©73|ĽŁ’ě©ÓbA‹-s&Ą[ŞŻ6˘Ć§żK޸6˘Ěç+D­ŤČóˇrQ2ˇËH,±˘ô1ˇÉ 4I{˙!ÍĂ5jŘ…‘ʨëĆéýt;çÜő879óŚQn?âţř nö7‚dlNuîşü.˘˘Ă7üŐ}O`Ýjů űQ ÜĆi\+ŮĐ`pćĐO×q,ŁQ;öÜ äĹŕµ@o™FLŤî"»~ťG§ŕî–&őR:Âi.ć]1†‹Ă=řXC"bĎá›=OŤ4Áéĺ$şÁü<æ/Ăuůż ő+ďăBv†÷“27[çŚČlOˇňO1N78 yaˇ‰\ÇěQem n§JRŇ@¬ł&§-€c-ßě`Ůőňs>ÉÜŻ®8Rś¤ÜÂ?aöăoŤť>Ć&Ú5Nve’Ć7ÓŻ¶¬Ë˘9©+&˝č I_âHn7RŔ…|âíLac¤±2(Z2× Ý~ŁiňQQŹYÝD?-…ĂAf°oű+ ­ž ľÚŻĘě\ÝŽffDS€~—|ß"kÝHGiAµŰ{¸.íHciPµŰ8;’f—ü0^ž5»Vv0ľëŢ †B§lÚ§&ş—ÄDEňťY!2QĐŽ–nVą}c¸Ä7‹š6ÔÄôYîb?řV(dgLžÎâ:?Kţ˝&V~gMĽĹEF]W„Ńâµ¶ÉußŮL˙7Z2ßŃÁçŘĄy)IE€ŰďŽ~Ű#cŕe)F‹Ó™#ĐÇĆ”lĆPCČ.$¶r¤€ďh¨łżň¬&ŮóďÉZ26ůŹ79^Jń˙v0%O—rťÇű+ąOČTgŽáľ=á„0j ÓúeqX±ńžęaşŘ·<=kh˙÷ŞKýGqP „!›—ň=xŞ\+CcW/¨ůţýĺV9Z‡ŁŘđy@Ćuą~Ńö« lBbKđŠ#ĎN‰‹­“*Ŕł24uő¸™K„·IJŮé+pHĆő¨žŻWl~‰ţN Çů$PӦѽtĘsÝ…Ú—CĎ{m«—(Ɖ:3˙挓—ÖŐ .AÚ ^Tćfbek(!±B g”Hę]] ńgg8"«ö±–'ŁEň]ÉÂ$±9Ô&đŞ ęÉ8°#Hi°ÓIWEËÎÉGđÉ&°ÝÖ$PňţÝבpľ=ŮŔx6ŠÚláŹr‘lŮŕä‘X> ąëxvd vˇ8Jaâu«Ž–Ěuü µ«mbňűGsxĘĎÉ&äéÎ qÔŐđ±ěíµ¦™5ˇĆŤ~‘ąž0 ›‰×eŠRqBůś ć)DĚ<›6ŕb±Ţy3J,ż‡÷h`açy4˛Ş$mě2 ˛l+Nť2` Z<ÔâťŮF(đ"vR+Śst(ĂŤ©6&2p};Ź\=Wł€öŤďÍQđđáĽÂmĚ8g´#€dŞBĽS‘§%¦Ň4ŕYĚűžßp‡Ď-Ú/@°.‹$ĺťućX.ŤIéDŻ{3©×)ه‚9Ü• 1ĐT»âÝ$OĘr iŰbNńŽĺ—8^n2š}™–žOđ€Jť3ĘÇűuoëĐLREVdYz‘(A3'ÉĄ–A~к̽¶-QÁP~IÖar°•ŢžöĄ"‹zŇ#‰ĘŇK[š«®t× I§ßK”"¨ó%#)§/vţšV,ËńúF ŁQěˇKH‚ÝÁ^Żť€Ăś%/–?Ć."„bPXcŻOíG×)ć)Ś„Jep•žAŐňŰ’CdV;8ň†Ž €§S;°¨ řú0P\Ćg]ű(ŐW*_f\©wAMŁZůR”•‹§W}ŻÉ;eŕ2ĺßŕ›*N«|äł3ąO`›j:÷EťĘÎËe$íęţńVÍ•§Ą˙‚•}Č€LJčő‘Řt(´ľG¶#™Ţ`üÄĘşŚ^őëâpÖ}ţ¶{s<­„Oď0lL¸ně_©E¸®ĺ_©D¸®m—(ĎÚéÂÜ-a]˝ßúńěÔü:v]ŕo•ř×$\ú+ő7üć8e K¬™»?—öyź>'·±âwkÁ^” Oiëűx6aÂU-­»D˘/ŃĘdątKç졀Ł:ň^q•r´‡x5‘Ľx>ŐĆBęĹĂlě¤k«ü~î?ţ[/Úď;6Ý®ă=[©]Ôł =¨Ľ»Zč¬1 RCţ=S˙ĂH]8âđ%D?$F†aÄ,z÷‹řHó…±ţt§ šP•!Ä#ďíËŻŐäbĐź9˘˙Ľ ‰.GłęEńFi®r?>“I“§g&­˘űĄ ™÷ŕĹ ÉjĺZ{ĚňĐV9¨+O˘čÁJáîBô2˛W”u®™ý#™kú„Ř®ŇÔ’F­2—4xh‘s ˙t‡gâ›ČD ?ŚJëNů—F˘C$1vš>?VŞ]JWtµ“ŢřD ,Ä­‘Ér8ňĽĐŃN[0ąŤ5ą˝F]Ň€÷Yě‚ ‘ś·®ŇáVB,ďí@„m2©P8 H"–SĐ;źĹO-˘ĚýŤhěŞ;˙b%c9r>#É/MRNHňeęIS/S]zhCbaseBΛ«m#3{¬;<ĺˇ!d7˝˙ÝÍV–˝~$Čx$„Ľe컕ŮĐ%ľ_ ™ŕBc3§4đ±ˇg=vűQ ˘©eš7©xă”ŮXŁ—řŕŠçkň\sEĎ:‹„5Qń1KbĎŮr¦ĄŹţëĐlď˛ŢYő~Őy›|Őül {…öÓ¶](¬­ü*zśĎýÍ=ŹŢ‡Ň˝Ś\óć“ű߀2źľ)?Ž+´5Á•6Á{ü˝ô‰?~ľłZÇBŻľŰĎbö×iVľŔ}޶ŞŰńjîĘŹš•öAŮűţ[sô•…îŔ<óÁsöŐ™Ôo‰+wĺRđ;ĎĺbŐé#żA „škxgę J;Órŕ{Gx=ر7x|Ş ŻŘ«:ľ¬5•ά—Ťwáyŕµ*«3˙©F9‚çAôP\.2µŁQc éW lŚÍ4!^­•TŤČ÷Źmţgű#ŰúO°_ţoŔJm¨@ZC ş]*]_¨˝[r(ý~$It¸˙€&4đ“MD˙˝˙ň¬•Jć­ožźf_c´Ç4A˙.Řúo•ĚtAΉŮ(aX˛‹R¨•Îęµ€#NĆřüaDŃż vüçŞ@–Bfĺ1Ć2>˙śc˙wĂŽ˙îâo„ĚE×ęlÄŠnŔ±a|ţK8úwÁ˙łľ&äźF1úwÁŕ˙.«*cAČ.ř—Żů#ąůÚŻępŽ5&řôď„Á«l úĄÓ]f†<Č‚Üë7 r†C0ľ˙Ó0Ć÷ź˙é·˙Tkl×üôv–řOĘ„ď?'üŇ˙ÓŤµ ű‹ŕÝŔwŔ„ü3Ě?‚ ‚ ‚ ‚ ‚Ď;ą(././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree15.tar.gz0000644000000000000000000000127414567004737015467 0ustar00‹Z›EíÔMo˘@ŔqĎ|Š~‚vfŹŠŠŠ 妼€ňŽź~u·‡v7ͦuťße áIţ<”ąëBüŇ»'ÍqřzBë B?Ď7=0bY¸\‡C¸÷„ď:Ő›Ş(wůÓSďgiV‡öçĎąyń«üŐß óK“;}_éséOs—ۤ˙|ěÓřrŢř×ŔÇ}ˇ?fKúůůĹ îí'z‡ô˙Ł?}ăwüSö}˙[Oôéí×5»fYôyŚ.ý!`i!xíĎ\ě=űŚóŃ7ďĎä”=ôŇU5–1l*­ß­'XžľNTo‘r‡ľeşĹ–±;ęĎJÜއýżÓoöoű˝öűţÓ Ů˙GČĎ •Ö'Ë(5Iő¦^´ó5YtCo$X‹ČwĽ8Pdm%"0Dě ¶]ÓőËáf¨!ľQ§Ü‘§bß<`mS߮ޖ%„QPŠ›QĽd-Grrbćm*š ązš5ŤÂXL˘®L$g=ʲ|ç‡ZD!Ńő˘C“µł}TVśľX6jÍ+‡Ł/'Ś%S–XÓI¨yŤˇétÉÓuć'“ŠŢQs­ŤşfęעĄDĚVe‹ş!ńřäaż†AęŚÔJćŇtüś›¶7gµ‚›tŐŕÜ2%¶geś#>]:N×Çç0>!WŇhÚL‘Gso4µ59/cl´Š‚/1…îy^(u·ĂZËvŞżĎťÖßÍvŽóxoŰ™÷“Ą—f;Ź@ŕřóľ\‡łĆmä|´™ÁsžL sŕ°`DŻxs:XŞŢYq[퇀—ҬRNkÝX8–ŃA6jÖ;Ĺ/b`7ťúšđí˛\mem(žŮIĄ.ĹĹřŔ¬Ä©ÎÉż› ‚ ‚ ‚ ‚ ‚ ľ‘D (././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree16.tar.gz0000644000000000000000000002141314567004737015465 0ustar00‹i2›EěťÇ–ăČ™…{Ť§č'±„'Ľ#H;xďA¸§VwŹF:˘˛¤QS}Šß ň0çňFÄů#r“Â˙ű— ‚0A`߮߮ „˘ż]˙ŕÄPG@ †~ˇßŢö+öCďęîÓŚżţúK?vm·Ń?~_2Nݏˇ×2˙®\ŚM~Đ×ŕź×ÂŔoŻC0oý_Áßę˙cľ˙Š˙ čŹúÖ˙<Ő?-ęäqý¬1ľ Śăč?ÔG°˙ő?JŕđC |Ľü+řY7đ?ąţĽ\ž˝H^7FÝqĺ¸DOII梩05ńÚË—â¨Ď‘¤îw ˇJNĆÉ űZ™Ů*lťfěăEc4Ű8F*˘=Ż ezC—D»GN[tš_Ďkç=ŐŤôL±…˝šG6hĹĄjÇ&:0á@+s á&Čx7jňĽ©ŞmÖZ~ťÄKý(0ëĽęŠéîE?®niŠý¬5)>«GĺžňZ˘ÉŐ™#Ę!@ł%ęSÎOǹŦë&¦ď·‚•SŘ0÷fyĚ^#gźčŞÜłíR;bL<<Î5{vśĐf075®5^Ş·\ o~s×jŹńĐĘšv8o ©ŽşQŞˇÖućiÜTş`$›ĆÁಔ·•Vdx&*ąN(U»B‡d…_Áî #Ę•†XŢŔŃ ŰŇ…”ôÇĎřů¬1ľç†˙Î˙ţö˙+ ä-̤cݱl{ťa-Ú±“\š8gšß5RĂC DÝŤŇĘĹ€`îIXpŐ%˝ń«4@ŘŰëx\ä§ľm$é˘nj}Ă[´ 7ÍdôÔ[EÄ^<@"[—ŇŃ5€ąŢóCÜő ’ôËŤ¨âĘŢ÷ć/|ŕôłĆřž˙Ń'ţGˇ·˙_iŇRđőFRŞ•ŘtĆčb4ś–·)"Lܡ€;± r‡^.ÓE®Ŕţ~2 áÄí;)š€R¤MŚeťzčł®píśΔ¦Ôüőv#\mŃđ-ËÎŃQŰSkSL ÝBőhčüĽfš’T›”T#>GÍŮßłł¸'­3Ć~Ą_…p±’'®@ÚĚÉžKŽ™ŐŞG/čÚÓ¨Î)†M=f 6_Ŕc<,#†y›ŕčöę°]šlwŞĂgbő Xýž§sQOgŘď˘ŘxI2/Xr˙ŁřŔ˙źż|Ď˙ľý˙EŮv€;ź ům´H¤çĹšŮî9óXŁĎÂ…Š’}¦néŞ7ĂĹ@oʜȡi "Ç;ń»°•’®*•XhžĹÍ7rµďhxźçŔŘßIN%N˘:ßUúÉÜőźĎţÇ?kŚďúü{˙#ďüď%DŕÔb[!ß–Č2ô˝"˛`Ó65O‰vÓĐX¨•â&PňĚÝŮÖĆ ń棳·ź˙ä|ŕâłĆřîţ„Ţë˙± ¬Ńîârőwş-ŁQt|ĚAŇ,ëŢéŤÜ١µęMשýŻrAج_˛}ÝÎśĄ0Ť(»Lá©Ö¸Üź;=‡D_€ă É7 Ńmä1ŃŘ>qK:¸Ă×Óĺ„/î*.f=€ÓfvÇDŔ˛›azĽů®ĺË~¸hĚký˝źlť*4µĺ¤C)¨”fĚd1ěĐŠ,çÝËä+npÖtĹx\ŁĐ@ô’1Ăhh E1†™ž~ö ě˙ź>kŚďćč˙ż×˙—€&†*Ňfmµˇĺ-s ËůÔBń`'ú®ňůäĆ”áâF“.3+ ŁŹ€Má^XlĘOMěGŐpć!“ŽÁ…§HB5„ł¨Ž˛f`YO(X¸}tŤ‘ޤ;ЍŚlČÁrÚąŮ*!xŔCIçzFńb‰#ź•/wĐäN?[9ţržúż.Úę“˙'â_č˙Ŕ!ä±ţß܇đ”źÜ˙Oô‡ż®˙ Ƕó·ţřÝ˙ó žę˙âţ|’˙ďő˙5·ş[r‡Â¬˝DµY¬@ÚÉr&ëPâTN`ÁĐO»~[Ýl4Ěţ˙‡ żçŘ;˙{Oőqţ‡Ŕčű÷˙/Qń~čă–üPm×ÄR&Ó˛‹%Y8ÂTĆnyTW¨ IŠ×E3*ňjadz€¸+n!ă+˝Ýö#ěul)ü4ëJK….Ćĺśmů„O'w"ŠŚŚş[c…ç+´%ă·ÖęĄ]tTÇŔc’łŔŃ4窲 źâó2Í [w8´ůŠžÄ»qpśUÓ‡€pń‚θrâr üs-Ŕ_ĚţYţ‡@Oö˙ďü˙%и:Ó° *m‡&BĘD,0¬’H¬¸')q·Ś7g‡ 5)yżĄ†ÓkĚŘą­×D''e‚11ˇ[Pĺäv!ˇ{¶TiŇ*«fŘŮ2Ďőɨr˘Eq© ,Xą»kó>Z‹a$ĹB+÷í ¸.±ČĂ• ă,I…™śŠś&´fĎWń'^pŞ–$8śĚnÚĘ=j©ń“ťůá­·í˙?|ŕ˙—ĺŕłő˙í˙—°‚PwżćKmď'¶}/o#ýD<ő˙Wö˙üV˙=ęä/ő˙§íCžň“ű˙ý?u˙÷oä?oý ­˙§7~ýÁ?ź˙|;Ěűíü/ ç?Żŕoő˙ęóż Ĺ~?˙ ‡Ţúż‚§úGaU˙•´Ń¸÷óż?Ćw÷˙íý6˙ŁňŢ˙ż‚§úż8˙źť˙ňÖ˙%ȇâküťŇbĘ‹¸ŕÄĽ«żźŠü˙˛üFńw˙ÇÁ Ux©ÍĂŕAĎ؇Ŕ‘ş7vŞ´h)ç5m%ŕtSŹsń\ô±K<ĆéŔŘAŤFD×Î`iµ°!ŽZ´ĄU¬–î›ăďc6EC}{Ěż’ă9F’Łć‡pŠź(%ŮIS†y{ľaoŠD”آË9â÷śôĂřŔ˙Ż;˙ y?˙ńU“Ç–V'«HV×ÍÇo.Ą$nwžbËîRďņr đz4K~ö°Ž„”.Żi“VsH¨•ľÔ޶]rvĚëCëś+›đyfyč¨ôRM`˛óÖ,ÂFv‡ě­«ÖŠ•R‚§íî©Ë÷i›č˘Đ&ÂěüöüKxę˙˙„ţŻ˙Ë?mzĘOî˙'úáóźHŕż÷áď˙˙đžę˙…ů bżĺ?ŕűüç—đT˙ç?(ň¤˙çýüßK‡éděĆ®A^7^üµŞş˛„µp θ†°Éä˙{çµä:r¤á{< Ľą„÷ Ěaď=ž~űHŠUHÓŃł q4{ú»eAF⯪¬ú3ëZ ôąĹćJJŰÚîŠřhł©;z˙ęf–3o“ůHęě:=’Ţئ‘h R4š {\ŠŹ ,€Dn%G±uLúÁšňSF¸g»• ŞŐž0¦ŔÜ43Ŕcł«`űŚF`áś×´%§ŕé˘ňÔ±zsÜiWę: (Ă2¶$‘ ®ŹüŇ™_bß‹u?8Ńĺ 4ř°ÍăĹÄťÓŠ[5ĐKűâöG8ČmúŞ ĐSŠY– +dĘsÂ_cú…ţßW˙ ~R˙÷í˙} Żë°×ö5¤ëśfíľçx„lw x°˘§şu¬ßE&áŐŞ†©żő&$­bÂc˛—Jť`#ç˘óݎ•˙Ź|ˇ˙·í˙ü¸ěéźőŹ}Ď˙oˇÝu$]ŻéŞ­`)¦PÄ+Ă÷xŰ{ÉNdÚúđ%1ݏf;/ŔĘj•mÜ\ˇ‰÷'ZĂ2c_BöEąłáe’“śľîJŰ@†AdGéÉPŐ$ż&ÔFžr¤ŰsŃ>é‘řa_fGµäe‹¦64$¤°ŕÎÉl®[_‰äęąĹÜŹm ƇOÔî¤ű@ŤFÖ˛PSńBFŐ&[±eÁtXDµ ZNpŘđłđÂąđgkđ(G¤B€†]l jĂO,î樭ČfńŔ@X/4¦-ň~ľĚm.Ť0J7îc@ÂÓ|zu“‘:ŁúllÜšĄ~Ľ-ČPŰ\?1ÝX>5$ gţZ}fK=í4Ob)1Ô‚Űp$eA=RÓ,@ű¬Z/*饲¨"¦–>úµYşf`…Şĺoµ|`{ÜŹŐx6]ů7Á!:4WťC#r<”: Ĺ’ÚćU*äzË)Xí•Ú©ˇŹs"Ž‹ł‘‚i‡á5/Ďçôú[ý/˙öţ/ě{˙÷-4ƵuN –Ál~ˇĐ&»tëŤÁjc“¸‰¨qÖs&şsßôá*ű6w3±*IéÔŐlłBîĄÉq†˝( çBÁŢ5cé$™¦=ô˘ĎZGˇ¬śÜ(ě× —Sô’ó‡xZĘN˘á—í„®ŇLx­&ŻĆRľ9{IŃ„!{ë uFFĄĚYŞN’x;^ç…Ľ8BŽ[ˬOs:l]8MŘ‡íŞ’žj=ţńćí0"Ě}i@.˘ąë®Ąds©NěÁä´T:e©p­Ş^H@VŻ5 ů ¨uěmU±Š4©K -qk/ű=Ȩ«ü^Ńě —ă‡CYáă«Ř~S<Ý‚·Ę‡Ĺ‡›Č3.žÂA\µ”Y 7wĺĹFáÍżfٍşĎ^n_-PŁďŰ[›*ˇ˙·ő˙˙´ţ˙űţź·@#UľňqÇX$(F‡imŮî'ą§U‘ŢľěŃŢd%¸‡„+ó©ÓĚ =[ň ±ë%îĽ ű†¬ÜńĐ\1y-})1›zw¦?‰6ĂZ#JeťĐă  F˛…ĽĐ~ĽôE]¦Ý™ú-iW$ł…UĽŕĽíődhPsŹeł$üŢ(őčn„'6i*ý>z>‡FDs{ŐŰ5‹kÎ ŁŘ›ÝK¶Hr˝Ń4̬üaĘWO¤ŽßĄš•˝‚Ę,Ť#µ °ť˛aľSŻË®C€Éo ĄóÉĚĎq.ĺ‚ăgsŽŽnZa A5|äT Üź¶Ěš/wňŹßčsąŘăŘÔxďÓ?W.ô©ţ˙ÎĐďóźwđEü˙X˙?ţÝ˙á|˙źš˙˙ú˙î˙űä‹ř˙Ôüď˙\˙…˙µţ …ľëŢÁń˙©ë˙U˙?Î˙ŕźś‡|Ęwü˙7ţ?˝ńĎß˙…úřÇç?ężo˙Ç;řÇř˙Ńő_ţĹ˙čwýß[ř4ţoöŕöOúÇ@äŰ˙óµ–˘T=NVżpíöžÄH+y˘/qé˛cT˝ęnĄTŃŻÍT_lrňÄxuaůÚA§°{Ťa0b^ĹǸB3Ă ŽQšě®K»•ŮQ•—[ę÷Ő`ŘŮC˘9·…‡iu`dË=µrB®4ó-‹>ČiĽźVG͢šUaźśś‹›Áëu%ŃâĹ™7xŕ`Čq¸FžÂDR3KÇCÓyúpQS^›}o·NÂb—“î¨: ¦#u˛ĐÔ€O«ĚĺłťV^=‹2t.;ŽŽĂăĆ-Ú#yľÂ ™®­˛pÎćévWŠ7”Ü„ńRüDŘ×/8~ń;#CďǦO“ yH΀‰dqÍ^†u"6~ü=ęqdËćŁŘ&ôŔéG≜yřĐŁŕ;I_č˙mţ!~Ł˙o˙×{Ŕď¶ĹÁ–B/×Äę`}řLHĂß“…ŹşLI™¶w7ăĺt żľĹ€ŘVÓą…ßfD `-l7˙ń -čĎĐpLŇK1Ě[±"+Ę­ÇŁ™ĘzrćÔÄ(×ÉfQi»C4‡b”©ľG¦sfĎ˝Źä°GsĹ×ĚF'6™ë`Éîž…¦ÝNI§®»vk”ŹEöZ ţŃ2`ÔBđajvxUă|„µ-Ź«Ń˝đUż›,[…ˇMiźížhÉRXn©Ç&vŰ.c¶ "dŤL:vëöŹŃkć]’ię«8˝Ö—3âCă|w·$‚.‹…/ëŃâµ”z,xÍ•iňmÔ-ăóŃ™ĐĐşĄ·müR‡`ŞÚÖÍâŞRöŤQx!kĄIhMť…â€UÜŔád"Ú8ÁÂŃ;_;ho¸ýę ýżŻţçďőżź˙żý_oP'®˛óv΢ÎqÝô§ŁęçA B*ŚxĽ[¤…§ó|5!<ב€§PS1»¸ye"GW›­Ď!łWěZůk×O˝ đ^]+|*JŽN•r^(1bÇ^ű¦°Ź­ĽĺË, Ĺô˘ÉšřĎu|ň§ç ýżÍ˙~¦˙ďúß·`[Ťô4ü(ZwĘ[$Ś©fËď—&^˘Ťű4°ű¤¨ç-žÚ€ăFÚ–Hň×E‰˛a ’ ë8䊏ÁźŕBëj#>‚€ŤÝŔϢһu—ŢĄŮ ýîR: DĎ EimZi‘§­NŠŻB’ŻĽ÷ç,ṅ˘&7ębńÓxu«BqRÍł«ŢŚłč«‰‡N‰»Ěúú°eÄ,Ô*xΦ˝`ĺ¤ěsśĂQźNŻ+.żący%ŽřYG< L)›ŻväM–»H¤(öW»pň ýżÍ˙á˙Ľ˙÷­˙w!ç ^BŽ–ÁěPČ(Č-{n%ű1GK˘G§ů…1Űö€ňĐŔ 1Đ»ş$Jh™čÔŹHáĎZ1u­‘+#rř5 wC“m]źŔńEń:AĘúşň‹©ëżź/ôŹ˙¬gü®ţł˙Źđ÷ţß[H ä‰Ó»ë$lě©c™WCĎ|őĎŐ´ô˛1ÍÄV«‘VW~ăzË’z%{(›ěŠd<đP%2Ěh+ĭś*Î1vUčqŘR°‘ž»Y$@6“»@S„n‰’¬ĎŞńŁăW»@˛č(ŕIŚé#Cššh˘±Š &AëĺâgälÄL€Ąü‡„âŐžĄ1§zhóäŻ–ŽżťOő˙Çůż˙ć˙Ŕ!äďţŻź6}Ę/®˙Oă˙×ţ/?íż7ţ˙˙˙ˇďóź·đIü˙ŔţO†!ŕ_ď˙Cżý_ďŕÓřżŮ˙E€źś˙|÷~ @KŕwĐ{éÓsŐ¨±*“Hż„S ÂT›(ĽÎ‹ÄŻa<ʰ-{”¦EőTߦ˘(ŕľ“ćQ>7pöWJčTÍäňĘŃŚ°ië 諣$ýA›QŽľ âÂ>rÔŘľ<ꀬśýRÓunÓŞľmźČVˇüľ[+Ň®í`x)ßB)Ŕ¸8c,±žad»?‘3QłĘö ä:Ťwüă'řJ ŁÓ°y˘¬;P®zśł5ĄżnĎÓŹÔÔŹm“ń¬‚ţävő˘_™˛ ÁŢ“îejŇśÖšQüň^µAk&rg€U<î®ĎOĚže]®Ö(ŤFú eĎőľZń˘ú~ć=Ń,čĆPiľ±U“@Ă]`4±k‹óˇŰ[ÂO«ÜßµýÇxĄĺW°N0\xXqßwüÎGë“&/ĽFâuÚb5uî9Ž©+EmçţÍňú_˙č“ý_ě[˙ďŕC[ ě¸cJˇőÔźyP!gŻŐ&ú|ŽČ‰’˘[3\ţÂLňx’'Sbş42‹ěĺŞË„Óő}Ů úwšö'ĺ ýżÍ˙…@żő~ď˙Ľ‡E‚JsŁ$ÖÓv'ČűźöÎm;M- Ă÷> Ç…\* Ĺc4Ŕť" €ŕ‘đô;jÍ.˛ŰnłV:ß´ťă™ü|«|ÔśĘt[đgĚîŕć}Óz ¤Ű÷(5›ˇçĺ(ěŔĘ‘yšˇŽÂŚ#N—rK4[aÎĘüš6ňNs÷¤ćzyš*yČĽů Äî|Žvłî.J…ÔĺĐÔ1™0‹°5X/{ŤQż;‹¦Ôj[OlÎoőqľ¤Öa¦ů´ÓV[§űZܶ{5ÜcCh7r×6č]'ŚŽ˝żëTš˙ďŕ˙˙JçOĐ˙}Ý˙!öżx(™˙Ý?˙őý/KSěŐ˙ĎĂůŹX(ť?ćý/űqţ;ô0ÓčrÚ±´$3čěÂ|¶˘xŻËމž¬37S„Qoźd/O…ş˛q­÷¬ÄI9­c¬‡ţ`ŽdjůB˛©S;z.Űź­mĎO‚±AgĘuWrÜIfĽ1¤ŃˇI!íYpśaA9ÉS¸ä×–]śčÜÝź˙ŕ~”ĹŮ€ŤýÜ’ÜUi·ÓčNćň!;¤=9JP§W'®©mZa–®n¶‹†úhÍ{®eŐţ®`„©Č?¶ý/K—<˙Áű,´‘‘¶UëŠ窞äČŤÝIׄ˛ôŢ2É)ÉJý˘G«‘D÷VŰÜN·}iź±µqš6Ҳ©'Í÷î~š‡+1食…ž÷NýáÄĎŇ4jĂ•sH_/†üü¤ÍIÇć"máčK5înă´ašBÖÝíť`ÓY"YvôőČŰ)Űń÷3˙uý§~<˙ťű‹Ŕ˙…OóůŻđďŇřűĚĚÎÄÄżć§Ćçgţdţüđżů¸PÝľ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree17.tar.gz0000644000000000000000000000174514567004737015474 0ustar00‹˘G›EíťŰjŰPEő)ţ‚öÜE?' n ˝¤Řn!_IGJĹôTEÚűeŻbPČň\:źŇô8Óy—S))¤8<Ďűľ¸î”ý«f~^o—Ó©űqyţţüëéŃ~ŢůrEüAXnŐp®ö"Řâż÷Ł˙łüCxë߇c^˙á?÷AţÜůx lńźű~đ]”k˙ŹżĽţ+źĎ;]c<¸ý7˙eôꇻ“ŰéúEţ ˙Ϧ˙XäÁÚ˙őĺz;ó;Ö-ůżřŘŤż Eůí˙ÓÓ×óŕi‡kl‰˙âÝ˙iě˙˙ÇÓňv¸Ć6˙ľúOňŹ ĺ?îpŤmţcőßË?Ë˙žŁ€mőżLő_ď˙0ŘţYőôźópaĹ?ËdĹ5ţ5˙…`ű'Ĺű?˝˙‡Đňď˙§řňż“wţŁ#Ď˙KLÓüß'ĺk˙Üůőź]Żř‡`űçĚ˙g˙>¨˙‡°öOž˙OţSý+˙ŹíźÔ˙×řOĘ˙Zţáý˙â_ůBË?|ţżř×ůË?mţżÔÍ˙!ŘţÉő_ů‚ĺź6˙_â?*ţŘţÉńŻúˇĺźÖ˙GůGpďϹ϶üźëü_÷BXűgĎ˙sť˙+˙C°ýłć˙ąÎ˙•˙!¬ýłç˙Yý?Ű?«˙Ďę˙´üăű˙¬ţHË?~ţźőů –Ţü®˙ş˙ ‚íź\˙•˙!Xţyó˙9ţłâíź˙Ş˙Zţiýżö@řă˙¸%p›ň˙´˙'DíĂđÖż;âđ§Ű¸˙ËĹi˙WŃů„;˙‡Ľ6ůOˇž˙éţok˙Üóżę?»˘úÁöĎ9˙›ý{ŻĎ˙BXűçž˙U˙)éţ ¶Îű˙9ţ“ň?†–ôű˙W˙Ę˙ZţŃçŻţµ˙ ‚ĺźuţ÷Z˙{Ő¶rýWţ‡`ůgť˙-ń4˙…`ű'Çżę?„–V˙Żýîýń0Űň¬óÝ˙ aíź=˙ŹuţŻüÁöĎš˙Çúůĺk˙ěůT˙ÄöĎę˙Łú -˙řţ?Ş˙ŇňŹź˙Gí˙bůçÍ˙Łö±ýsëżň?Ë?oţµ˙íź˙Ş˙Zţiýżö?@¸÷żgŢ_Ř–˙çď˙Đç?!¬ýłç˙ó÷(˙C°ýłć˙ó÷(˙CXűgĎ˙µ˙‰íźŐ˙k˙7’–|˙ŻýĎHZţńóíFbůçÍ˙µ˙‰íź\˙•˙!Xţyó˙¤ý@l˙äřWý‡ĐňOë˙µ˙M!„8Śß’yĘ# ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree18.tar.gz0000644000000000000000000000171014567004737015465 0ustar00‹ĄG›Eíť˙nÚ0Fyž`łť_Űăt(›ŞmĄ6©oż-«ÝeAÉw#ĺśZK©zŻ}ż`N‡¶Ťź>nć$„š¦:żĆ¦ ç×ËňňúĘ&†Ş¬ë˛uÜ„Ř «6ŰjÖżę•_ÇÓĂa»Ý<öOűߏ;{\{8*ţ -§«˙B=Ű›`Ľ˙TtżĆż€÷ţcšç-p‡˙Ş®ńŻ óź>Ďđă?Ąn\,BJřWĐ÷żűň°űţˇűŻ|k'şĆYpçvÄý_…¦›˙ĂD×˙'ř7üďŽţ» €}˙Ç—ă©ý'¬ăëY&Ö˙l˙_´ť§ ®qÇý_2˙kňź&¸Ć]ţ™˙% ů/&¸Ć]ţkü+°üOŮ ¸«ţł˙—`űw®˙Ě˙,˙…÷ýßp˙+°ý;ß˙Ô CţÝÖ˙ ţXýżöiwxy>MrŤ1ţŻý˙ެđ/!ó_÷ü§ąä?‘üWBß˙2ňźšţźŰżoţŮ˙Ičű_FţÉ$Řţ}÷Ě˙†ü{í˙jć Cţ˝ňźšţŹËżwţÉ$Řţťë?óżËżwţÉ$Řţťďęż„!˙nëú˙¬ţź_ţÓ\óźą˙)ű~oŚ©˙©×ü'P˙ôý/$˙aý/Áöďś˙°ţ“Đ÷żü‡ţŹŰ?ýźlÜ*ý»í˙˙% ůwËŘ˙K°ü»ç?ô%Řţé˙făVĺß;˙Iô$Řţťďęż„!˙nź˙ řW`ő˙Üňźâężd˙/áŻ˙fYç?ňü·„÷ţĂáßćÎóń/!ó?Ë[`Ś˙T—ü7°˙—Đ÷żü—ç?$Řţťóß„}˙ É9˙W‚íßą˙Ăü/aČż[ţËü/aČż[ţ[ŕ_ĺß=˙-¨˙ l˙Îőźů_‚ĺß;˙ĺü' ¶çűźú/aČżŰúźó_$Xý?·ü÷Ň˙ŻĘ˙r˙s|ÜúźÎ??ţ/R˙ôý/$˙aý/Áöďś˙°ţ“Đ÷żü‡ţŹŰ?ýźlÜ*ý»í˙˙% ůwËŘ˙I°ü»ç?ô%Řţé˙făVĺß=˙áůo ¶ÎĎĆ­Ň?çżßĆ­ÄżwţS^ó>˙/!÷?eÝcLýOťxľ˙KGß˙BňÖ˙l˙Îůë? }˙ É˙%Řţé˙dăVéźç˙năVéß-˙a˙/Áňďž˙Đ˙•`ű§˙›Ť[•÷ü‡óß%Řţ9˙=·J˙ś˙~·˙ŢůOÍ÷ü'‡ęőçČ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree19.tar.gz0000644000000000000000000000165314567004737015474 0ustar00‹¨G›EíťńjŰ0‡ó(}‚M˛,›=NĽQ¶µ%É}ű:KŰ™›q°çÍßG!ýCŕ/ş“îlĺtčşřéănIBUŰćókls8ż†X׿__ŘĹ릩ShŇ.ÄŘ6aw“}W/ü<žn77»ÇĂĂýĂŻ»˝=®;oHËéâż ˇYěK0Ý•ę˙ ŢűŹŐ2_+üç6â_AáżZ"Lńźú˙CL!fü+úßľÝűĐ*_»™®qÜ»ť0˙shűřfşţ_Áżá˙áŕ迏řW0ô|:žşqĆ<0=˙×ubý/Áö˙ĺî{×{šáWĚ˙šřŻaĚ5Ă5®đźń_Â˙4Ă5®ňńŻŔň?g)ŕŞüĎţ_‚íß5˙˙EXţ“÷üŻ˙ l˙Îóźü/aĚżŰúżÂż«ţ×ÝďOʧY®1Ĺ˙ĄţźëŚ …˙śű?—ůźú?ňż‚ˇ˙uôęl˙ľýźŠýꄎ˙uô*ú?l˙ľý⿆1˙Nűżş%ţKóďÔ˙©[ę?,˙ŢýźŠţŹŰżóýÄ –ďţOE˙G‚íßyţ“˙%Śůw[˙S˙—PúźłîóĘ”ř˙úüGh˙ †ţ×Q˙gý§Áöď\˙'˙Kú_IýźýżŰ?ű˙bÜ&ý»­˙‰˙Ćü»Ő˙Ů˙I°ü»×˙©˙I°ýS˙+ĆmĘż{ýźű?%Řţťç?ů_·őÂż«ţçýüGŤ ü·«:˙/s˙Ż„÷ţĂͿݕç˙á_BᑯŔt˙)DüKú_G˙·Nţ`ű÷í˙žĎ˙Ä˙ň ýŻŁ˙ËůŻl˙ÎĎ˙%Śů÷Ş˙4Ä cţ˝úżM–ďţoäţ_ ¶çüOü—`ů÷î˙F~˙E‚íßyţ“˙%Śůw[˙sţ‡„Ň˙?5%ţ§©˙ ú_IýźőźŰżsýźü/ač%őö˙l˙ě˙‹q›ôď¶ţ'ţKóďV˙g˙'Áňď^˙§ţ'ÁöOýŻ·)˙ŢőÎÖ`űçüçbÜ&ýsţó۸Ťř÷~ţ+_ž˙b˙/ˇô?gŢezţO!rţ„ˇ˙•ôX˙K°ý;÷X˙Iú_I˙‡úŹŰ?őźbÜ&ýs˙×۸Múw;˙ŹýżËżw˙‡úŻŰ?őßbܦü»÷8˙[‚íźóż‹q›ôĎůßoăţK˙đďń nŃČ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree2.ini0000644000000000000000000000041514567004737014751 0ustar00; Single-depth directory containing only other directories [names] dirprefix = dir fileprefix = file linkprefix = link [sizes] maxdepth = 1 mindirs = 1 maxdirs = 10 minfiles = 0 maxfiles = 0 minlinks = 0 maxlinks = 0 minsize = 0 maxsize = 500 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree2.tar.gz0000644000000000000000000000034014567004737015374 0ustar00‹ú;AíŘM‚0†á…8ÓŞÇ1Ę‚Ť‚ž_Q‰łŃDM'cň=›ŮyK“2•®ó+WůśÓ<9'š'qŚ·ůŕZ˘÷ě3ą&Ő}¬»Ó8mKÓ¸cĂąß˝ľ®+ă›ű,/˛Ě?1ÝúďűrMRk|Ţ?2eô× úWŰľéŹď_‡čLőgô× úGSý=úký“©ţý5ţ­©ţý5ţŮT˙„ţD˙µ©ţ-úký7¦úăüŻâŮźÉRü˙řÝůú–ë(././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree20.tar.gz0000644000000000000000000000172014567004737015457 0ustar00‹¬G›Eíťán›0Fy”<Áf Úăl›Şmm•d“úö#IŰE [DßEâś?ůc‰h_ŰźłŰÓˇmSř\,I!5M>Ć&‡ógUuů|Ą!Wu]•9§"ÄnXYěň˘ßę•?ÇÓ×ĂnW<žźţ>ěíqíá¨řBZNW˙)„z±—`š˙şóźĘŞĆż‚[˙1-ó Lň_źçĘuĆż‚ž˙ôeW`’˙ŞËţ ýďż}Ý˙üÔý«ühgzĆYpçv‚˙šŞŘ…™ž˙!ř7ü?ýw+ţ ý_ާöwśq^˙«*±ţK°ýřŐvžfxĆóż˘ţkóźfxĆ]ţ©˙Ćü—3<ă.˙5ţXţçŚîZ˙É$Řţť×ężËé=˙ćżŰżóügý—0ćßm˙ßŕ_AĎąÄUđôú_†Hţ/ačůMţ#Áöď›˙Gö˙†ţב˙Gň ¶ßý?ő_ĂŻýMý—0ćß+˙Ż9˙K°ü{ç˙‘ü_‚íßyý§ţK°ü{ç˙‘ü_‚íßyţłţKóď¶˙'˙—Đ÷?gîóĆôú_†©˙ †ţW’˙ł˙“`űwÎ˙Y˙% ýŻ$˙çü/ÁöĎůż7n“ţÝö˙Ô cţÝňÎ,˙îů?ůźŰ?ů_oܦü{ç˙ôĐ`űwž˙¬˙Ćü»ýţ?ŕ_AĎ˙5ük÷‡—çÓ\Ďä˙Ň˙)§”đŻŕÖ˙"ň‹‰ţ/ýßňůŕ‰˙ý7ëč˙×Äk˙żŠýź‚[˙a‰ËßbŞ˙|é˙×°˙—ĐóżČ+0ýüW†Ŕ˙˙0ôż’ű˙ČúŻŔöď|˙Ďţ_ÂĐ˙Jî˙ő_íß9˙ŁţKóďv˙Oý—0ćßíţźüG‚ĺßýţźż˙!ÁöďĽţS˙%Xţ˝ď˙#ůŻŰżóügý—0ćßm˙O˙ }˙Kü ¨éőżě^ęż‚ˇ˙•ä˙ě˙$ŘţťóÖ C˙+É˙9˙K°ýsţďŤŰ¤·ý?ő_·üźóźËż{ţOţ'ÁöOţ×·)˙îů?ż˙”`ű§˙woÜ&ýÓ˙ű}Ü&üĎY÷ß^˙ËîëP˙ ýŻ$˙g˙'Áöďś˙łţKú_IţĎů_‚íźóoÜ&ýóűź÷q›ôď–˙sţ“`ůwĎ˙É˙$ŘţÉ˙ză6ĺß=˙ç÷źl˙ô˙îŤŰ¤úżŹŰ‚˙ţ—ţO9%ćż„[˙«č˙wé˙–Ď ţĽú_Jý…Iţsşî˙č˙ đ!˙zŽîŢČ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree21.tar.gz0000644000000000000000000000674514567004737015474 0ustar00‹˘¦ßEíÝ˙NŰVĆa˙˝«đ4ßsüă\OŠĚ ˛ w?;ë¶j[ĘčŚŃÔçR”’çăhҦ˘Ú<CN‡ę=ą”n˝MĄ‹ő6RŰ^nż¬JŃőĄäÔ•TEZÖUu÷®ĎęËž¦ů8Öuő8žÎϧ›ëŹĆiŹ'´ďćßÎ?G”wű"xĂůçÜ.źOąiŠóßc_źĽÓëŔ[Î?•˛žWÂůﱿś˙»| üËóoűľmşvy\j"zçżÇţ~ţ7źŹ7ż|Zţ«ü™rUwŰ=…ë{šćăX×Őăx~8?źn®?n§=žĐľ»~ţ·§»a9§ ®±pß·˙|ţM4ą)žZĎżë–/‰:6¸ö«űÁχ—p‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ‚ ľOTöľ›ÇaČé#Ę!Ňú1˝LópßnOwCDŢ౬ďŰő6•..÷S{ąŃD“ۨRt})9§6W‘ş¶4U\űŐ=Móq¬ëęq-ĚśoQI Ş ţůGF$ąŻEĂ˙őË ‚0A`żŽżŽ „˘˙Î/ôůD Ăŕ_@"ü—ż`č¬ţαíéú—żü2ŻÓ8ťMöŻW¬ŰWLčkŮ˙¦ެMţ eđďëŹ(†|ô‡ ţÖ˙+řGý˦/@ůÉ1~ÇŃ©?†ăżŃô—ż€?y?ä˙ąţA˝!4ó0€±* ‚łĚs¬e8÷á$vłÉß Şé¦Ťé¨ #’¤łĆDYRöľ(ĂË$«Ĺ`RŔľô„÷M*°NJŕPGĉµ1÷…aĆ@uŽ:fŃF×MŃIÓÜE ŁŔ­zĎCTa\öáę8(ŇWęf=íb¦˛1>”žT¤=aKŞĄf’‘»ňJxÓpĘm—˝\ŞR9P™já(ÓÎ'ŞfŹ?bSŮš9™ŃŰ’V"j=䏱Ł?zbá9¦!­ŔeŁsče”ýŤ`ěĘz팔5ąÖĄç4–I٤[7Üs'»ĹűĆ;_^ÁfŞ~u íg.¬łćK,Ç!tśYL13ɶę˙ĂŹüýäżç˙_Ż!?{ńŮů˙ćřŰ˙_AmĹĘ®g1łJ´ ł\ ś"ORáˇÎďářŽvĚQ‡AT¤óĺ!čCŁÂsAŘ–Ă„`Ť`:ú#pî źZ©Ç”§˙A«ý›ßň#˙Ă?9ĆďůGö?ţí˙Ż 2¸š.J9¨,D ÉŻ… ¨wq‹$ßÚV§ť5-)lŞÎ»’UB\…,NŠéE´ýqZJł1ěβ뎊W®ŠÇ¤ĺ,7Ąß{â|Ă^Ő@Ç'Ë \Ťŕm*+^ή(Đ{7¤b ™]›tŔޤäî#R+l86¤ä#¸#kÓ‰3ŔÖ#çCéŚ'ZB¦…k ;®Ľ‡Ď·fć{ý-$ËđěÉcŃÓ o¶lř7a÷ÎíKuH/“uB.Ó/öä…Ž…—Š• «ŹéTÍžTi®]űĚfˇ“t?ÉÎbąĺ{TęŽ!ÚQżD•µŤÓ¦LĐČ…ÁúwŕľŇ6Ms×nä«0 á]é±§ćńçkaaČťxoŹččá‹I˘A>ë;ĹhKÄĚ8Ę—ókŇć˙ö˙u đoő÷ŁŚ`ô×ţűî˙ľ„Ô˙O©˙ Ĺľó˙ź<ô&_©ś21ˇříşľIŁ@Á]î˝Ĺcw%đĚů´.ݵÜýС*4ĘjtőžuE·(ňI§·ĺťvJ›“ź?˛ÉŘą;J7î@0yAĽ•Ęo ź ˇ " Z1N±Jt %kj±[Y°pí|O—;ă+ŰĚjZ v毹ŃÉ4±](pŢÓIdw®‰TSéO^T2ęŞz Ö&‡DŔČÔÁQľŠv3BuŇzp$Y“·ź;“Áv{łn÷á*˘ŠÁN>e±zIvë|¬@­`ŇĚĺęÇNW“óŇ!rEcÝ8#Ż-¨řÚ{ 7˘&`ý°„™Q~»~3]«ÔV ‡ŁX?n÷ęŕĺć}°d€V˛+xň•4©'Ş ”ŁÜŻSŇŢ" R¨á öäĘ©2T0~ćÓ<#0y™÷ć x5Í•i÷z¬q!~ľKďb!˝iJ•:MŃM1ŮĹW=LçhŔÔ­ĺ8ivŮVžâŔ¨Ł78ŕÁp~o•+`“ôţ~ÔŔĺk§iŹÍ–X5úŠRXĺ$‚ö-‰Ţ:lu»e:ݍ ĎĘ_NU:Ň›šŤGď[y=¶ ŕR§Ĺi2c9ÉĄ¬ `zXęI_Űh†ŐhඥĂ=”ÖÍčzd–ÚńI¨ôŚąč‹îŐ NG~­wĽ­Äľ?Žĺ˙Ż®˙AđůüÎ˙_|t_Ti~óźČŹü˙Ĺç˙Žýł˙?%á·˙ż€:ŐéO•ĺç§Ëąń©»bTáŔhšg Ůč´×0ĄLÝ ŻąW 4óĽŐ˘lQ+&,…éĘ®ă17ěˇX$ő{2á§ëIĎ€ÎISYŁĽ{k+n¦Ó3X«7ĘÁŐâl^„¤b@Cz<Łő†Â«Z.ČJČ=»Ő&„›ćě÷\ĄOÚ—ť;†ýŕ7ľ7mąí¸zD5î ŹäC6l,{źănŘ=¸Ú4!˲ŢH7ňúŤWÚą3ka^¶fĎĆĂśĽED Xwź]Ľ‡"ŤíwNŐ"÷Qř:¦r5jěTç6°óV‡Ŕ&kqŇź,—§¬÷é3ĽëŔm µNŇ’;á;Ęc›\Á÷eĆÎϱXÂůBa±3ůK°»(^’Ž8ą÷—xăWuލŤÓ˵/ŃÄş€ę;ĐČO|–ď´Î¬ÇŽĆK`Y-Şec Čë$]ź`“P×ßAŤl{ ‰gjy:;DŻl&ĎH¬Ň~Çíá”ʆˇ‚î#‰&CząŐv™,*FŠŔŞGDçÍl^ŮžĎ17Žšbuâ\¶Ľb,YÜ>dŇąť&Ü·YŹßŐg%ĆgEFű&"ĽlÖŘ/÷zQ©°ÂzË+Ě<·˘nŔ’˛#Ź·—ᨱňއŚgs3čE{DDŤé}źąËÄͨ1Dpt ą %2ş‡đ)j™Ľ @±FŹŚę­šYK<ťV%Š}‘;׎—ű©JߦŃĐ=:zňnjÓ]¬h‰YswÔ«EҨősD˝šž5 ŰIX—90Łe˛ËUś`ă —ć‘ßš¨4ożs]¸cęŢă™=‚”P0Î={Jíat $uŻÖq{z†3‚‘A¨śTnđ±ß 0« Ńô+`đÖ„÷"“ZÄ>ýŁüŹţäż{ţ ˙ś˙żź˙| ý>1Ň…^ćř:ł›˛ĹÁmŁ(Tł'áŔ§ §’ +…#,VH%>5äDđŽŐo0ŹłZťxe6Łŕîr¬r°/ˇ\˘OlOЧo†kŞDA2ŰŚšŮ‘éPačuˇ?öŮP€Ź' ą®ěpjÝőÔ2}ľX(cR›O&ŘĺÓą‡ů“ô”Źý4Ăî>@Ĺr.µ°¤jož!-ę'·Ś@Q#Ś &1&łesŇ»žŐľâZĽçÄÚßµQ¬ ¬érWD×Í›Ŕ2âÄnäŕ.WäĐÚ"ŕL[ďNë- Rę´¶4Qął>ŰÍSÄä¶ę)¶ü,Ą§\•X;ŰÁ“ÁRINÖvŃ<#WÜ>‚ ő(M\Ĺň^ţ#˙˙ěs×ëýŹßúźřö˙WđýţÇ÷űżő?ţ“cü^˙‡âŕ÷ţ˙'1Y1ŽÄ§D­˝Ĺ_3–Ç?mŔ6gl S‹vCŢaö°Ęů5nݵnŁŞ3ďŢjó ďe+ġş7Ł ůZťqz¶+›$7Ě´MĆŰ[mNÉ2#W –Ąš@â:é™7öXÍ´‰óg`0Ęß%÷®©e#pĚd¸AF‡¸kö‚îý:Ž&AF$C˛eŁŢŰŃJiˇžŰh f˛Ů}°CýjÉ·ăDŢÚ^xGo(Ŕ] ©CJ9ÖmhŹ;ŠŻ ŽĆKąňňÍz…ljł;ą˝Y>Š1Žű#+yTíŰ}°Ş+ÂjČĂú{ÁS+ÁÉÓy€ĹÂŹéůľ“wĺ@¨{čÄ•»üĐÔµ˘ ą_Sf“ľWtJ)X‚~z4Á¶­–>w®§ýIłk.ú đěËŤ®}_Ňć>ýW u“ŘěÂ7.&^ʱ@…‰Í;)­—Šs©*ěę"ë>e*°ˇŞ†¸jX¸öi}čfę”ËfbĽÂpÁÎD©Ť ‘Qí·f^qc´ĘjťNűäj×V rŤď§÷ŇSŻÄĎ]}ć”0 # ” ¨M. ňbsúÖŠŢ z?/¶¦üyYŞŻ:Ńp°I°s%†SŮÍQ榬Đ:Ö0Íő1lM<đ.ů¶Şt蘆¤Ă›Ď ^Lrż1r,łiđtAtČ÷Ůήkdé(›:3%—»cłĎ ŢÔ÷ÄjÚŐħ2eW_ŞÇúôń Ĺ3ö+RW65vü:®âŁÄjlžĆ„&ˇŔ¶ĽŤ\ŚŤĄˇ^|Ĺz@µ2/!§ľü˙ŢQéŹň?ń“cü^ţÇŕÔßĎż (Z#Ýűś ăYiŘź«š”«Ă—ݶ®$,Z¨ľÜČ®:ie_e3éĄtŹŹ¬˘ŃĄ Ř9;Ea0©ËZűľÎľ‡%”sšKâxÎŘ/·öVQÎDúBŽZ 6‘ťB좒vIÔÎĐ1覄Ńת$óĺ“}¶7ß».ŕëňQnLË@ňieˇ"’Ůýw{g¶Ü(˛„á{=ĚÄ®KöEě ¸c‹± žţČîeěž>ÓÓ¶Üsşľđ•CŠđďĚŞĘĘü+/řŽj&ídHdµŰ 'dpwŠî:SŁdżŹÎđŞ 2”qSo—)pdŻ§Í”Ç%Y­MJ‰bŽć$ŔTÄÚ4b˝AqRę]Žď«–ĂČ›XŐ%r‰äęŁ*8€ştď‰7ŻÎĚ®zmÜl㌠Gw l,! Ňn\vE­-ňą0¶1YUhކşźvYga'ć:Î-lOąFL‡†CÄĘP[÷eŻ}í ŤłýqʢÉËX?ÄXě\§ŮÄ»B‘ÄS÷ g’ŕĹĘIă•c—EťH±«lę†3–Ŕ ăĺ¨ŃŤŃX×o,ÓđL!Ă+HB‰á"Ş˘@Ůx·iĆs'Ĺ;óc>]«MęgšżňţXĆIŹ~YďglZÚîKfC—®lÓEí™ĂíH\ ć”w ÂvŘ»m wśăcş%˝Mí·Íx 唺e˘Ęiřŕ]š}ĘĄiRĆĹ^Ó ˛ŃYč$céíJ“ÇYÄ —ťD±9G–LĺĚëţÜ:·sSú«ťť&ëf,µd] ŠEŃŤ0&€·0´¶ŮcY§[§Žşď·!éĆQ!âŘWq© IkVĺ´ßd¤ůŤ4H2{5ŤEÜ÷ÍV–ë3 Oö%ë7,'-8±ĚV>§]vq×Ü śHE…ÔoÖ^O :E!Ť4Ć÷0Bâ9Z—!ű­ßË˙äÇŹÎ˙÷…ě˙?\€V}žiĚáŰ|+ĺ§r¸"©TŕCxĐľö­C¤"“¬ČeČdŚŤÔŔU'ŰVš1[!‚ÖÎěŮß D§ë9şŕzâ&ë8TÁ¬3đ4!;—´Řäz$÷ýšů[ŠuçGÝł¤úôtYµíÄMن_b¤3óQý{ů^üďŢř;~˙(ö×ú?â˙!tlÚ„ü@®"¤ąčă=ŻŤzĺ­âŞ žĺÎNľQ!H™ ÉN8Ü ^…)ň’PŘÚá€!fĎš°éßPg3g÷ßsľ© ]ĎĹ:5'Wý°Nčx(RkQ[RöM†ą;_° 2ÎŽ"7Ě–3”ËŐ°4”fnäŮ&|ć ĺĚfĎ;X U_‰µJŔŞË5÷˙"⾣iÍ&¶Ą¤…gM6™ôÂîU#UůínľÔ饫ÓI´ËţÜm${ď–íé8ďąf´Ě=¦*QGŰ”([ő–\’?:í1VDQTő’eSÉRŠS•!ˇGńvű˙KIźă˙}?óĂű?âŰóú?„Y%¤ĺEĽáel0˝`_Băt‰sG–S„¸„´$žDČř™· 6˛ŞâłAňűŐ˛n‹o^%˘÷ÉłŚ)ľFëŤÝďGÄôŞz±n^ÓËđľFôJ3;{o}˝xĆŤ»ˇ(Éá ĐĹP=oŇfĹÂŤ(:¸!j„Űáĺ°“€–2ćŔë ŁOÝ^,Ó”¬]´¨Éą¦bĽ¬'‹Gĺ<•Mˇ$ö|aĺ˛ęP«eVé:ďî““°HUÔ5Ĺ Î$sŁĆ&Ë‹ĺ$7“Ľ1 ć™ç[w“RRAŁśš%„ĺ3I1KA\QÖĽS:–v-¸Ú¦`|ÚŁ°ňÂDgÝ™rŮaÚ‰·–ŁpYÂ^ŕ¤űqŹmN×í%tŃý`Dx€Z°ň‰Đ9˛˛ŹČł‹^Ř!t®Č˛ëZĂ1‘%7“K)l‰jśÔÉřDśÔJŚîŞÔ#®ľA:z˙oÝřů™Öí_ă â˙lâVXWr}Žý4ź©ĚćSŰĺ©fŽ"›=¬să®[!„v\5ÍÇ‘ČTpa…gÚ«ź®ńů0äôĘŔ(A°Ľ8„ĚȻُ+~>ąe×1Ú«EéI뤾rϬĺ!ľLÔ~ş…ÖqYćÓşCÉ8K„^ŇWT“•(Äă!'Ż-™ÇŤ™E~ܧ©»÷)-´R5…ňŰÄŰă9đŐťSXBr7Oâ04Ö\ͦ‰áäÁwÝ^•ć¬Z.ÇtÎŮKĺň Ü\h  pŔŹJ%ź*}5§¶;Ő”oE3ŠOncÖF´˝‘Ăąi”[ýĐÄ©•¨íIŠÚ}ɖщóĄcľ™U 3C«9+3OÎNöš/]_ĐylcSHT5ETé©2fĚąČíH,¨Há덊—‹ }˛µpÚŔKIé2ťCfŻ»D§ťô+¤5F×G”ŽŻ™9]:ÔŮ]XŹ‚ZçhÝĘîTP “zsćă‰ŰRÎ ‰-ŘÄłH÷ľź˘0ÍÔ¶ëFď$B˘?ńšŠXD5,ÔrËh;{YŔˇĄsŐ\ěÜc ;×ĂŘ皬|¶Üö9!ćčËV?Ătż1"#‹Zót•dBpĽBpŻo0?×Č©FwRÝtőě‰ˇŻ„3]ZČ)ÖMWŢÔc}tüVŘzÔTÇş{u6›$¸ŕńťb:žy4R6mş'\*;.l®·jÔ5­Â6[?ĽŽç˝bĆ|ĐE^YĺÂ_waĘÔł€ăŞ,-’Clu˘dĄĆövƤhtŇ~äµ$áűĄ"ź)l+™SJŃŰŞH‹ö9ůŕv¬Ä´Ńh6˘§<’Żh$đŚŻţé¬ 7«đĐ|ęđŞ%6ťÉŐŰŐ!˶ĽţC<Ď<×ŔüĎCx­˙/P˙A?ż˙ć¨˙€ú¨˙€úĎËü˙ˇőź/ůô?P˙őP˙őź—ů˙Cë?`˙˙P@ýç÷ćuüt˙'±…áĎýđ‹ţŻ÷2&}â7Ź˙ďé˙qýź_ęŘýs/ôëi^ô˙Ş?ţ Ô±§˙“'˙'Đ˙÷^ë˙!őßOďż}‰ď?đţx˙íĎř˙řýv? >Ż˙Ä‹ő˙­$zÉo˙ßÓ˙#÷źÖyňüŞ?ůŽţä@˙ŻúżÂţBÁý˙y­˙Gß˙Ń…`đţÇC÷˙ŕţÜ˙ű˙O۬÷ůŽźZ˙ďkÁóúüźÂký?fý˙Ó˙ď‹ţ`ţçQ˙?ŕ˙ü˙~_˙?đ˙Ă|a°bČ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree3.ini0000644000000000000000000000041614567004737014753 0ustar00; Higher-depth directory containing only other directories. [names] dirprefix = dir fileprefix = file linkprefix = link [sizes] maxdepth = 2 mindirs = 1 maxdirs = 10 minfiles = 0 maxfiles = 0 minlinks = 0 maxlinks = 0 minsize = 0 maxsize = 500 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree3.tar.gz0000644000000000000000000000113414567004737015377 0ustar00‹ţ;Aí™AŽÓ@}”Ü€n»Ăqd1yç‡<’­'ŮO©jăM%*˙Žĺ>ßnÇîTJ)ý4ŤŻ×:ŤĺőZjko×…®–Z®ýĐú~ęJjťşËxîÇúĂŹ—űçůréľĎĎßž>}ůűënóË?Ţçý‹Ľ_˙îoţż>Íż•śuě÷ßj˝âßÁĆ˙I·Á~˙c­#ţ¬ü÷QóĎůoaă?iţ9˙-¨˙㏇ü3˙Ô˙ńŰŔ#ţű‚ężeřŻřw ţÇ ˙=ţ¨˙k†˙˙Ô˙”áżáßú˙áźţcAýĘđĎţoaĺ˙´żöűoě&6ţ“úýÇÂĘ˙ń‹˙ÂCóĎţoaă?hţ9˙=¨˙ŚţĎü{P˙!ýźţgAý‡ôúźőŇ˙éÔH˙§˙YP˙!ýźţgAý‡ôúŹőźŃ˙ö +˙Ç?ř-ě÷ßŘ˙Llü'ő?úŹőŇ˙ ę?¤˙Ń,¨˙ţG˙± ţCúýÇ‚úéô ę?¤˙Ń,¨˙ŚţG˙ń ţCúűżń_K„úʇ•˙ăüöűoě˙&6ţ“ú/ýĎ‚úéżĚżőŇéÔH˙Ą˙YP˙!ý—ţgAý‡ô_úźőźŃéÔH˙Ą˙XP˙!ý—ýß‚řOéżř·°ňü˙Â~˙ŤýßÄĆR˙Ą˙YP˙!ý—ů· ţCú/ýĎ‚úéżô? ę?¤˙Ň˙,¨˙ŚţK˙ó ţCú/ýĎ‚úéżô ę?¤˙˛˙śĆ/<ˇ:đx././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree4.ini0000644000000000000000000000042314567004737014752 0ustar00; Higher-depth directory containing small files and directories [names] dirprefix = dir fileprefix = file linkprefix = link [sizes] maxdepth = 2 mindirs = 1 maxdirs = 10 minfiles = 1 maxfiles = 10 minlinks = 0 maxlinks = 0 minsize = 0 maxsize = 500 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree4.tar.gz0000644000000000000000000002164014567004737015404 0ustar00‹<AěťÉ˛ŁVş…=ć)ü—ľŠľďA ­č=čé+]×á ߼®J+Ź]|RhKZ¬Íć×ú·ć1ËĐ˙ůéOA °_ŽżAE˙yü•ź qAaäËó ‚ź~ĆţÜ·őż,ÓŤ?˙üÓcě»~-“ß^6N˙Çëüëüëřaţ§ţi9~‘äĎ: ţ¸ţ(á§ţďŕ+ý˙¤ÓŕŹëŹAvę˙^ő‡?‡ţ§˙ß«ţČçĐ˙Ľţż…ŻôĎË&űrüŢcüň}ŕ8ú»úC ň˘?ýôó[ľÄ˙rýóţ¶‰|·YZCŔ8 ńDYq€ąŮˇKCţĽxĄŤŚcwlľR16piÎđŕ5üčŹpňđ˙áď=Ć·üŹ˘Żó?źţéĄ*…™e0@3}~-h>“BŘÝZ"!´J9d0‰^<üAAÝćq5¨íʆŻvu„.”5Ó©cfŁSHŤÔzÁ<ÁĂŐXÄ”ŚtđÍŠ@=<ź,…É2›·}g"—hHü–6‰@ ׋gÁůh»5s<ЧÄWä„Ăn°v{Óřśll•—ĚW’úqť".°Xş?ݬbÉŤŚ¶ÂVoř§‹YŰŽ}ŽöM)hDcÄzaşý–ô ˛Ů“ÇĎJXgŔßSVÄ;z3Ń˙ö“ŰţGľ÷ßň˙—G_ýŹťţvëßţ,?ů=>đ?ú˝Çř¦˙ń˙Ă_ŢĹé˙7@űű^#LBSĆ-ÉŚU{8JEe’D8LĚ›P<ˇŚ·`¶“¬"ęś/ţ6üĆ˙ßżđ÷+Ľţžőź7ń•ţź©ţÖß«ţź¤ţú˙-Ľę˙9ę˙0xę˙ľŇ˙Ő˙ě¬˙ý(ĽVż/eËQ†)d¸#Đśú‡ Ϥ/{2:y—©ër`Ú`Y}Ý,âŽYµ@WŤOä83ž2TŻ>p`›ďO9Ţý<Ç'ŠŘ ”Ľ ÇL°ĚĹ]B´.5ď×éžJLŤé(Ö„“=Á|ŞBÓĐ$óęŕ@Łqm} ¶žŹ,0čZąč÷{0¤°v¦ÔÝůÝpśŠć™vÉF+ť-ĆM-żJpDCërKLHJú XOçËďđ˙ß^˙‡âőţ˙ô˙[XŁĹ0¦şš˛Ţ‰dË4@ý*,k&ăXč×"g\ŠÚ¤ę¬xÓ2á*oKŕ;tLofI&Ćł(ŰŃ]_Ć«c˘fÍf‘Kú¬ńâ~âTňED¸˙\kEâ§;ą§-4ř˙íőčË”órýGN˙ż Ó‡o/ÖÜa9‹d˛-“PŚęQóŞn ŁÁK +AÚM«o3])ţĂĘ8ş/Ô5öqZř/Íţýź@Ďú˙bŠG—Ŕ=S—Â; íčŚÄ<ôhĐ2ůu5 ŢÉacWŻw%Ç«3Ŕ‰$Ň-Nç˙ řŔ˙ß˝ěňÍűčő˙ůű˙[¨ň ‰Őëµ ZH¦Ć ř˝ÂriGq\nśOM&ŁvůŘ‹Ů]Řk ¶ě>™ń®Ô۸’ÍŐ(3ůÔ=®)Í4.DÇx*Ź@yłďéÜ\iŠÜ\6ˇŰeŐ5—ˇ†â§>ăcÝ€ hŽ«{Ž.†›Đř·ě—,’9â9Ż<Řq J,kpĎß]` ăęQ&Ę\lK’ńĄÇ·u łĚF( żuç”ôGůŔ˙ř÷ă[ţÇŃW˙Cçő˙-ĚŤ)$Óę˝%!WÍç§p aĆPµöB˛L«q‰!OĆ hś†Y:Č:!upŁYhčD|8«`-U NŰO16Ő&T‘/NK}ĘŔ2Ć|˘Â)]'4dń‘ę>©¨‡V”—Ŕt#Ë!ź µútěŐl2«›|$Ţ„$óoÜę7˘;BW{'2Ë1˝t »\Ev%_*•ş}>±! ˘—ąŁőÚűEË)'JÜPFđ<  ±\)Ä1 цî†×Ebç]+7‹ häŐ]{®w85´ç’j}ŁÝnô%T±›ôđ"˝7¶d+˛^r6Z?%€u™çĽŽéqâéIÁŔ «®ź±řN5ŠMQŕR6ˇ”MâŇ`‘ćúĹ—Yvd‹ş˘to“ÓT:p|ÔäL­F›E< şTŠě<’ÎţĽyí˙ß{ŚoŢ˙Ł/ů˙sý˙&|•¦NÄÔbÚČw׏Ą\Đľ1ăI>IŰ.%’â¶Đ_NŮhGRŐ ’˝“­ ×ü@P™\8‘’%}hOěIHśÂÍDäŚ<ŻÉź™üO~ď1ľąţ‡_óxú˙X)Ú?ďÁî€9:ôwČ!"*€ć=B“%­™/¤b§%˛€©@3G–| Ţđ#wŠCwO:Cć7ÝÇ*—4B]ÜĂ>hJ\ž…Ś.M A;Ϻ܇ťÔ1‘j„+HKé’ şŤťą>×&×n<Ô Ě…čź|fäsr Frl ’ ”k(ęé|«©šÓ…«hŤŰŐĄ&Ýj/9§ś˙7żń˙÷~ü řoä˙ÎüÇ{řJ˙Ď”˙;ó_oáU˙Ď‘˙;ý˙^ő˙$ů?čÔ˙Ľę˙ýwú·ô‡OýßÁ«ţŘçĐ9őŻúăźCôÔ˙ĽęO|ýĎýżŢ«ţäçĐ˙ě˙z _é˙ú?°×ý`>ëżď »¶= =ŹE \ĺĘ&ýĹmĚ­Äfˇ‡“»ťj„0ť—*PfÉöá`I±gÔ_„ë,sbnK!óÄí›ŢU»©úĽ‰Ń”2q¦ŇŞŠŇföëĺ‰ĘăŢ‘tĆ™ďűqPńNŚ_+‡4Î0î"Ĺ!ÝD(¶Ě°ř[fß9Ä÷XaÖGîw›vłţRínkµ‡„ă 0zI7öŇŢ(B° XeG! +E¶áůGLó]fÜ<Ô8‚u˝ÂŐ|$| í¶ ń@^â®îŚ2ŽĎík‚ĐEČşa–ČşŢ7lnÂş:‘&4ý¦wř-Z±ă†jęâXb¤Uř+ÇŘ?đ˙űű?ň_gţű-x~:č•â nŃÝçgzG‰ç—8zĐď7M9Ô©FŐ,Î@őQ´V!Y%Ńčât™9šá-xř"{Ăv…3l+×0$eíE0ćŕ/lŚ˙>đ˙űű?ú?Ó˙ďj|Rn;ţ0qv“°ąŻć!OŐjŢŁ {wÄȸÎ}±\sv4‰€G_’ éH%~9ŘŚµŘš*D%{ yć–f°ňiýżř˙íýÄk˙×yýÄTŞP,6<Ú5Ů Áµ 7}5E 'ž­%ĆaWáX™1ą]7ÝďB-Ť5zΗ7•Ěć ĆU×Ú  Ţ‰—iŚŚQóž`˝Ų„ç\53LL®Ű™}#"_%‹§ŰdYç.ŘZŇôe-ţŘ—÷©Â5·@ěŘÎZ‡ˇÇ’֩Ε µÁgY`Y®"SŐŚ)* 1őrögfA…/qiˇh,+­5sKďˇ:,Ąk9sÁ83ěe†ćÁ<'Ä#𠙤y™j . Ś‘ř#Yw »Ű˛J˛¦2k#3;U}YóSf« rq<Â\Ć1Ö‹/§ů4őĐ hŽUeUm˝ WBš{Îبł.‡őä }Îh?ÝÇ=§Ě}á=$‹z€.fß´&8Wě~Ëł "­L©ąFjLt:tõ˘Ţ=ö bEązŔL^=«Xő±} ­'ëYĎ\Lµ.á–é i0]ňŢ˝©sţśläZXrPŤöÁYňýé·ByxťľYrŔ+Á@Ö8C“{Ťú<řP—!~2ÚT»¶ęĎ~I•ŐĹŐÓMs·đşm1u53ÁdÂÂ"a=Â1-GK±Ąý h ś˝ś®Éđ^ŇŮÓlňDďÇ%ČĘ–ÍKĂĂáŕČ]ź´Úďťî"ăć˙÷Śßř˙Oű@đßÉźůż·đ•ţź(˙}ćßĂ«ţź$˙}ú˙-Ľę˙Iňßgţ÷-|Ą˙Ę˙ Řë˙3˙óňŃč§am]k«BČ/.6°Í†Ř,·Üר-€Ů2z‰9lyÇĺ+}c4ěÎ t€éq´&ązóD× žíĘ2Â,â詚X +ÚĎÚÔ-ŚţîhšënĚŘ´D—ý=I$ă>ü˝óŘu;˘čś_ÆĚIĚA$gLbÎY_ďŰ€~¸Ś6Úz·m­©pPÚUuŽvńŁŹ2×á( C%®Ł!…çČ1˝‚‚-¸… ńxčÄć*u,~AůĚ7µŽ"iâäé/i&Ç•Ęp,SfôŃ1PŽ—DvB EŹYWíÉ­3¶ÖXÍ‚«$6¨Ň1™\ŽQŽşŕŞóĚ•ęw÷é˙-ţE˙˝ń˙źüGý˙'˙ż…_â˙“ú˙O˙÷ľÇ˙‡ô˙ýż…ďń˙!ý˙gţë-|Ź˙™˙üĚ˝…ďń˙!óźźůŻ·đ=ţ?dţó3˙őľÇ˙‡Ě~öżĽ…ďń˙óźČç˙ź·đKüÓýďË~żÝ˙~ö˙˝š8l$=z‘VŤöĆďCßÚ$(é"ĽR A’¦ŘP×ř8Öjm9ÂÓ†o¬ýŮÎűĐŰ î‡E<äH‰¬×3čZ¶…“ŤşrŚ*dŚ„pöúaEtč:uý\εHá ®Ĺ=Îb%ÎBďőŠőGóB˙oź˙üCó˙×ďT•^CŹ %/ďéŁ+n0LŁS&ôąząÚŔĄyŇÝ’ŁE»íbbMµ–@Ż)^Ő)ťĄQŻpŚ„‹‰öÓ MŚÖG! tVâ58—Ş®0B7ź>âp±Ă8:ĎJë6¬–¬ ­haZH©­$Á™ŽľŻ”‰7ç‹b> áżĎ ýż}ţ“€ż×˙Ďţß÷ŔVGMč•bąGaj„ßňEĘbă®Őçhö„<Ŕˇ8H9u SŁŽőa{á>Ż ¸6Xę¦&w-^ăńďAˇ<-D°Ů·Ś{nŃ=ĎšˇŇ *0“{ájÁĽŢÖ¤?“Ł'µ–O;÷ÂčI­‘˝ĐçÉŮÔѨ?Ż ’pÖGěQ‹c =oŤŘ÷ɉˇr$Áę•X§KËŔ°EgW'řXź8P°y$ˇ¤@ł;™©,ůhł“Ăţ”ľRŘ6IÓ’t†t |}~µ6íWÖňb}é7;¬3 Nx­’Ťş2áń”Ž5żÖ{T8i[±ĐŁ'É/˛M…=ĎT?ĄšĂϤÔŕ´«:(4gJşÜ*G*V·sgYNú-Ä[ŹT†ÓhŃiűÖëŰ:#ť×m-AÎ\™-:/FÍ š•4SW·±zřbM¾͡ SpÍ *mľĐ˙Űç?˙řěóţŹßí– —•?f»1űÎüUęcµ%ŠTäÉłŐ=TžiKâ9Ělăvs ¬ńJ|¸Ł¬÷$«±ÜŽđ LtÍAł§L‹ŠÜtŻr\Ľbąă‹˘ÇÝXÂÜHn\SjA˘E)ĺBĄ!Ψ)şć3qs3đŃlnŢňK•GQv0a˛•n†D(úÚăľňH·ä›„-…UyqTYÜüĐşFŇOšPŞt¬Ď­Ő„OrGL*ŁîęcĄ˘¨Ô0â†Ű®%› y2pŃOÜ39´TĘĄäąT§‰ćfÜ3„ǨŽńĄ¸íý)Âë(´M“«C6śš’:čęöĆéř«ĄhÔÖ° ·ĹVî–%ŘÔ*ĄÍ*€^/Čĺ>ĺQĐ4’2¸­d! çdÖVnÇ ®&‰.FłJh ć@PĘ’ŚËű4˘;:˛T.ć_Ú˝Đ˙Űç?Ař…˙ëS˙ßi\Ö&9Úůç§çţ˙â…ţß>˙ ßĎ˙ĐÇ˙ůÖ„GdÚAÎ%BOôäĄĹ=źŞÔˇ ‹RLž[(7­´9ăśĘć jűU‰›´Gíó“+ţţĽĐ˙Űç?1ř…ţ?ű_ŢÂhËTŢd]ŮÓgL‡$ظQË/éä«ńÔo­¤ŠŤ! hŘÇĐŔÂŚ_©_‡R†W y9¨GôĄFĎu”|7›jO@Ôň7M\§™ŮO.@ÜÓ—=ﵜ~DéIóĐrm†íĽëZőčůôrˇd×JCŘuVµĐ¨Î†ŕqźéëľřŁ­ť«Źr:8Ú‘Âý'0Ŕ)ć­4‚ŻhXC;çu1Ža|ÄńqvŢQ˘÷ŕiŚ]s·rĆęQBt§d&żjŹ–łôŘF)žÔŘ0ü§KLëb‰^N¬!-Wě ‹Ö•uY9ĎĽiŹÔťČTŃ]«{`µ‰É–Ę’óYSqŘŃ̉ŕ*L‰tM”¶• ŠÍQ¶yąU“fóśŮř&ńTkY^­ĆNnśÍ—^¶ŇčD^8ňęUŇöQť ?ź-©”Çä~…ź“GhMâ–ŚéHEťŹ:»cb­Ó<”uˇ„íÚ©U´s˛”5· -š2Śöyɧ»L#˛{פ•«´˘»ŹD/ÜNć4ot¤-"YAŃľýe˝ú»˙Ă_Ü˙ü?o@0ÂĺKe0Źâ`)ŐĂhrÝnÚ•ň#–8}VŽ"ő6š¨îßlĐŞh@¶TÓ®˝7·OűNő÷ÖžtsHřS» hŐSńLÝLŕÖ†őžÉ—·/؉YĆ6)+ă–Żź[îŞë]•ÇĐŽ’0[Yäf łÝČ8«ź]Ľö đMPô™ %µ˘?¨ř37‘jr¤Üß›RO!›… ď~g9UŘÝݬˇ1ôŕH™7U›†r žq= ›8:‡Na¤lş)hŃÎe}I]Ź!śÁ÷©lŃűŧ?ćĆkČŹĽF›vDa;ÝAÎŘ Ń`f¶Aď·Ć5T*n¨E(ϦőpEĹeMH!ŮÔ-ätEľá?)}ĽĐ˙űß˙˙ĂÇ˙űrřA/˝ďd‹S[Ąč­xxőűÍW‡§2„÷č• =ł:&¦BçŐ.ŢC Emór›®­ú-żiţśgMuŠçăi“§~łhúš˝Ä*Öˇ ý¤·Ç Zň™NPh›±0€BC¨Ô‡k‹.Š+ďd€äŰÓO̬‰ öv%[@ăÂ0>)2§Ż}Wo!Ç‹ůŃLÉąÓą¨ řą%š4 ExÉ˝ľĆ“â.Ŕůă`ÁÁô›rÜϵľŐD 8ZÝ«K*”Ě+Žűŕ€§1Ąpž6§Q:ţ"O¬‹R÷p}‰äęÉ—·Ť Ł$»•( t]mgѮޣq®ň$9ő®Ě§*]02Z‡¸eKGꇵÇIG»ű¶ŕÍ™Ó1ÇAßⲱŁ%z\L^Šşî"ĄNŚY3m÷•Q 2ŚŠBľNwÝUźžŕç"xŘ54ÄYł™Ëĺ€KÔł¶˙dryˇ˙·ű˙WţěŁ˙w°ieŃĹ2;3Ľ¨–Y€uN*1Ć`$žN; ȲBň_R%0 ¤˘¤ŽÍ’ÍRóëŕ|óî»Ę˘1ßźĘ ÖCŢ”ťágŔkÁKśT¨öÍ~şĺΨ®îŰaĐĘ•‹j_U™Ľë`ŰhQŃÁŘ^yYsT zđfa‘^RŘyŔt®Á"·µŤ„ĘŽő,źq€_ĹDĆęo5mEčT¤z˛ŇSFé\‚y$l ~Re !ĽL1 ‰/Ł(xŰđ]zü¤:ü»xˇ˙÷ż˙á•˙˙č˙höCPXŤ‚iZüŞu»Î‘{Ó–éö8ÉaĐťő. t ^ y«űë´n¶ňČě C/8ewS8ĂÁ…t—3C8™‘‡`«Ŕ+¤˛ŕ”ÂâŚóŇĹXILž,­$jCr÷ꧡŔŐUÇs-ѲxÚ65á·ő °AFhď‚®T M^°Â7ÓrĄ`[xe?QłŘ› ‰Ś™kŮr@±2Ă©řď*©Ż›0L‹7#nőOŐ˙…ú'˙ęgü;ýI˙ł˙ů7QÜ­…˘ÄŹ(ţOyˇęŻ~Ćż˝˙Ăż÷˙đçţď-PĎĹžˇZ‘}ŕÜÝČ–ŰęéŮ­v+ łL ±J‹¸©‚Ía¸łcFé­‰đP‰5×54ö„xkmĐ´@Q%ĽŢ‘SľwaŽĺTëÔŽpŁ®QŠĘµźąńőµ;´÷ܸ–ú)Ë^‹$vXyĄŮă8” ~xUö q°¸6}ÝČó?Ú»Ź%Y‘+ Ŕ{žW%Ţ{_;L…÷îéŐ ő˘cî„:BşÓRH|+–,ř““™‡d&Ŕ•hĄXýÇl)ŹŘT@™ä2ĚpŔŽ‹ÇÔX‹ŔÂvŮŻßqövqnär‚-łYş€öY``żŃĐćń†d Ö8€™Ü–Üé̤˛ńzÎhwĹ処öQXńĄ­Ż7:fĄÜxŇ´đ^ĐmďfçS·Í;dý’(\¨öäé”A‚¶[žňÇŔá3sý ą-KcëÖŇ…Ń›´l"Źi%ęu¦Ś×Ň(Q¬"ă°©;|ă2Ë–Šo¶l‡2XG} 5+hÚaďCM¬Mš¶ŇgWF Íł)%Ľ*&G(Úź*9K©ô<—[w˙ă@˙kţˇßţě~[˙Cň˙ç;˙?B ö#'D¬-ăóťÝ4¨ąIŹľű|ZJUŢš&´jĘHx¸îŔ+X]+Ëb×FĂţxĄžiަn: g9és÷žú ĐÝ™D\,eĎ&·ŮOÖ[ 4Í3iIxNů¤7Fôz™`MGťř’g»—<,®;(¨h_}Ě>ÖĺŃĹ=D·X/Ń.XźŹ8#ž1z)„Q‰ó›}ôŻůGţ˙˘ĆżOßĺÂ~ýţż×˙Ä„ÓkŤŮ‰R‡ö&ŁJÝfŕ{€|N 6K«Płh§ůf Í­0ńÝpŁ…/_=ŇĹvŻŮĽPĄĹź˘ÓŻ]LÄđ*ćôÓÚĎđ‚Ksî”ţ—ůš˙ßŢř÷é»üĂżě˙ˇ0xç˙Gt’ĽJ zŢĺÖÁ2lZr»O2ĐpŔĚőd‚;Ă[Ń ©`{ó1/Hۂ٪ íˇ—Ţ„§µ1TnU0Ž\<ěö+f±¸f3ąn°_®Ń/ď˛5 Y‚˛*Ҳ·ÍîĐű»_˲ÍŹĽL(gě0öl3™'›7ĂÇ{L‰ )‡fŔ7wµ+µľýĎţ“é'}Í˙ooüűôíúňÇő˙Ź÷˙˝˙÷#,JݡNŠ»xĎ5ÓŰ͵ĺvž´ĐéN×˙Żů˙퍟ľË?öËţ?zŻ˙˙Ę)÷%Źąöş$˛ż^aKěE1Ó±"HŤÜą!mw«_ĎęCŃNhwżšÁ–ŔéŠl„s®FW¶‹XîUZű‹3]7ƧVžB$­W‚÷™şÓ ’a€˘ëŘąnŕůC·%ÚxÚ‡m=˝Ó;…,ˇ süMŽIźí\a5ďRUňŞĎżc\ č; AFPČĺČđˇÜŰhB«Ż ťnÔEËŇ6Äw㛤fÜ=Ý!zYKk6öĹfŘ«…×Űő[?mÇšZΗޮřŐ»ŰŇÚWůk™ŢýN'ÉwÎ> y•Ć5I:Śby^Y´Ëf$!qűŐ°‘†Ŕ ŰŽŔĹƱżć˘ L–dÁݬđä«Őĺe]MW”}˙˝Ź2l‰ÜJ>ćaűĹźużć˙ŻúŢňŰú˙OÎ˙Ŕďý˙ˇĺ =*'A¦$©'ĺWݤUÓ&ŞyV$˛&ţš;€Lcű6ÁÁiÝ‘ \@—ҧä@šp:UđvÉ4Ě"żŕh ď°7[9µ€:g_“Ż LT)ŤŇ NĽ5 y%±Á5=1$1<,Ă5˛ NŇ’-,>/~dĄšĽú"&¤ňP1'âdUĺJ#ĎŤŚLJö13áěřé]çŃä p>wö™I«=e‘Ë% ¤’Ós… 75F”ł~Nś1{SRě°ŰžB]ăż'FÇarŔ¨Î<Ł.ü˘lI'€‚óĎľ˘ü¦' ¨F(îśßks8úD¸ÓŁ>]|”'xq8ŽM5đHOIDŢĎ-Ýlľ¤fDq6łW°}ˇnh±GFíD{|]±•Ôe"âöŚ/Y|_ˇ˘4ŹçŠé‚Ě­ĺ,u›<Ú‘;źĎÄ[ĐŻž”N˝"vŢDüg, -”Ůd­Ĺ%#cĺ Ą˘mµS)Vi҇ŢEíŕř\´Č&÷ľNŞüOÇŘŻů˙퍟ľ­˙Ů˙»×˙~Šm­ńyNťąµÚÖ-CC ÁKőćŢÇí"‹ÓZ52Ť`?.š™Ă›4&s$D)Ě·q.¤”!¨ĂýéŃU .e˝^ˇ/WzŰ›S"GC=†+@lĺ„PáŇe1öx8ăŹ3Fř´ęłójË\·˛6ň=  ±Ç ҇a÷Xů1 !řyéynÁýÁ¤ĺôZřnđĽ•Ćűw? µUĆ" ŕp3ęŹp`p Ş>t8‰>RK­BĚ~;fvj"AŽ,§ŞL3v°a=©u2Z\ÇßĂP"Ę䞎đŮH_Šŕ[ś ćO'Ő]¤&ć«Űx¤Çý,t:QÇ»yv3ĆťĎú•{`6[J h Ę*©ŠlÖU·^ ŚfźXĘóă®–§zWB·Űív»Ýn·Űív»Ýn·Űív»Ýn·Űívű7ü ËłA././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree5.ini0000644000000000000000000000043214567004737014753 0ustar00; Higher-depth directory containing small files, directories and links [names] dirprefix = dir fileprefix = file linkprefix = link [sizes] maxdepth = 2 mindirs = 1 maxdirs = 10 minfiles = 1 maxfiles = 10 minlinks = 1 maxlinks = 2 minsize = 0 maxsize = 500 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree5.tar.gz0000644000000000000000000003245314567004737015411 0ustar00‹€áAěťÉ–ŁV˘E=ć+üŹľŇ÷­D;Ł @€@˘űú™¶ßŞ(…+ËU‘Jů%{ b’6].ç·átÂţç—ď ‚0A`_¶_¶ „˘_·żó B Aŕ ‚ăż€!đ/żbß÷eýĆ}ĽĹĂŻżţŇ]ŰMçôĎď4Ś˙âďüńFţŘţM¸}őźť‡7%ßë0ř+ţQříç ˝mv˙Oŕť˙ďtü»ţqAQyóŹAşűŹţá×đŹíţźÁŁä5üă»˙gđč} ˙Äî˙Ľóźź›ÓŰöł÷ńĺóŔqôOý#řŁ˙·Ăĺ×§|?ąZ†Kfyzw·P=ZFm 5ap"#˶$µ‘:ëDJ©·:xS,ž‘Úŕĺ*aQćłyF\No›UjNŁKf´Ô!ĹMú>b NV´ę„Šň/‘w˘÷‚±KŁ<ŃvŢ@tÍ6zß\@„JnÍYĄL۰é)eEoćp„»‰ŁK6ÔW–ş-ëŐ,Ë)f®©§„Ş˝ĄeršĹˇDŔĘ{ôwâe‹S"É˝Ě69 Óí‚j O-đŁ?ďWăüĂź˝Źoĺú(˙čž˙gPÖô-ŻáŇčţ˘â`Ľ(\üŇKCgĄ$»%c±Ňî…“RiúHÚŚčŕą°&¦łřNŤ Ä[:9lq±" ±ˇxYFUňb(›ďřÍ•ČÍmĎß‹ń.˙Íą­żÓ÷?Aaţ‡‰·ń?üťÎGďřÉó˙ţ?˙Â˙wţÝń˙W˙(ňŰüß~ý÷ŢůĄůż}ţç)<ú‘ůż=˙OáŃ˙‹Ě˙íó?OáŃ˙kĚ˙í÷˙žĂŁ˙ĎoüGţˇÝ˙3xôŹż†˙˝˙ńýŻáŮý?G˙äkřßűOáŃ?őţ÷ë˙§đŕ_Â˙>˙óŢů˙A÷˙ ˙g˙0ď÷˙žÁć3ALC°ň6wőŽ ?!3çX|Ź‘·4EvŹî¨ł’r¬‹m#XŕNČ1„ bÉU†&{ĹóVŐ¤ű=› nŮçóíl^¸a„R@8’Aă80ę¦4Ďt‰§usŕz['P—™ĂµĘh»č›ŇĆwsî™;U¤é°ŰZžJšňx¦G©Žâ@ńfŢ:YŁ#§ .€€b0łë1R4–ăÂf3š‡ćíýúv±BÚđÚ/aˇ‚@ĐśáĘ“(伂88.â˘'B^ëfL˘›š·¶›`N74.ăX’mÍ (tĚBĚ6Ď<é ěZoQĺl<íĄĘI¤aśćTĎw; 3.HZĄi}…Ýßä:SBŰ3,ńĹEŤj=ç’8ţÔsW­šçDÖ’o‘%×Ę•Zĺ>TWáMťĺ|Â`ĐŐ2gţwU?Č˙Óď˙#čůGöü?Ť‰€Ľµ®ŕÁ56iyK1Fg‰"łŘ}M7q/]€ pÉTÇĽK¤¨\ĚÍO˘ą’n+%1<Ű2TŁ˘t›˝őĺŐl@Pő.ąŁ¸žĺŽ PI ë—Ů׼­Ô¤bá…ß.ŰőçÖ5¨5ZFŠŮ°NIíîućöú,Kx–¶ŔˇČ@k0D‹Ú9‘÷Ő xy/”Dć†+]¬öIé8˘đ±ćH‹SŐ1Ű[ĘůG>{ßĘ˙Űčđ1˙ŕž˙g ł74So1k1®Âx«éĄţß|ˇěü­x—˙Wč˙ č—ţĎ—˙ ˙v?ňł_Î{~ňüŕ˙»Ś˙ţm˙_ű?oţ‘ßýCßůŁÜý˙á˙óoü˙řWú_ţµ˙…ěó˙Oáť˙Węíýź§đč˙Eú_{˙ç)<úŤţ×Ţ˙yŹţ_¤˙µ÷žÂŁ˙éíýź§đč˙Eú_űř˙)<ú‘ţ×Ţ˙y Źţ_¤˙µ÷?žÂŁ˙éí×˙OáÁ˙«ôżv˙Oáť˙őü}|ţľß˙}3`j‡Ą¸2GWCŻŇI®ôŐuďÝPĄ%Ĺ\5ż4e^©+@ěÍÇhuTq_U†Ű˝Ő"żDĆ9W|!»ŻťXg±ł7}DÁ¸ť3‚†™ Ë€ˇ+Y«¶IVŠŞëiĄ`šx˛ç1öPĆóšÜ=˝†ńäED«¸śŁ6€WČŤś †ŐˇâXŕ]rA[’µ7áď7­˙>Č˙Óű_Ž<ö?=˙Ď@Đ[ ĽYO“€Asś˝ĚYi3µ— Ű Ö`Î$äASAčľćržÝ"ŤëHD îîDO%Ů!ôx2©* /.ąPłĘ°éÁÁ!Sś#\csU¶Şí T•Ł‚9·ŽŁSć!‘(!!G*ďO“ä˙éý/‡÷ü˙ p®Ď‡Ć(`żK`y]NѵڂuúT5$„›/Ý%ßą;©3“Óú] \SnlŠÍY§hiuŤÁđbPvćbepŹŞćޡ˛ˇYŤQ«dHYvž-®ÂluŁT6x“y’ę9§ŮjÂĹ·„b‘bO%d7 „9'IŹ[ 0'ć6/I˘ŐQ_B3Ó[č˛ő*Ú¦»#úQ/S gYŁ ýŽŇ}lâ`Čś$ĹäD$€€H!µP!‰c4_őˇc)PäŤiŻŁqH{âŢlŇ’·š÷Ťb`ő> ôVW2č’3Vż: ‚Ę`mĚLH”#Ł’µĆaĚ«ę$vg®ŚqOç3ť_+‚ĹĐ3ŞđŮŁ‡ŢÚüzÁÁ&S®Ł!EĘ:Ń•ĘS2Kec‰dŮIźdŹdŐJ˝t,¬Rqd52x'QĐsmθZ1s“lŤ›něÁ D-¤4p®0ى÷z8‘ä ŰÇmeŤŠéšćşLĂî12Ď\x’:¦ŁžÁ,WĂŔ?Ť’>Č?úŮÇŘ·ň‚Ř>ţ˙Aݢďçź—ň˙éÓ.ßĘ?ŠAŹß˙űóźĘÂ·ŽŞoUŃ˝§BoŻŘŰa†äˇÉ± ĎŮQ;i¶â…Ý]&Y–ÓďćŐIKp“YRPŔ«Ń—ux°~pap 4÷V—QSňřPĘb řaŁ "x{"ÓŔ“ô |Ę„ŹşL´†Ž™–YuiĹá%˛ŮEr [nřżçâüź˝Źo^˙ŁăÚó˙ČYl–hg!×NgúˇrĚĹŹ1»Itö\içS'¬[ľ-đG‚:ôĘČ}#XtĚőťąE#S¸Ć[#Φäě 8śIKi¤vü…ą¨Ą“DRU´ €ŇR7&•ń-ASŮ|"˛:ťĚ;JŕZľ9˙?ô?öőßOÂŹ7±°)¦pŮ5öDýdĽË˙+¬˙ţşţG đ˙ţ˙çżžwüäů˙˙źżđçwŔż˛ţ—@[˙»÷żźÂ;˙Ż´ţw_˙ůýżĆúß}ýçsxô˙"ë÷őźOáŃ˙‹¬˙Ý×>…wţÔó_‘ž˙¸÷˙žÚÝ˝ÚäÍ΢ÍCé¨Î™4qşa6ZŤ¬¤č;ĂŠŁPłRFĽ˛„˛µNŰťŞeŔ=;Ó*Ö®˙ËŢyěHŹ%Wxϧˇ7Kzď=wôŢ%]’OŻęŃRăŻĹęÉnÍÔ·%ŔD"îa\sn„ÍT P˛YÓ2â0fBŚ[ż˝Ŕ± ÂŻäuç¬ĂŁčąŹ¦ˇvOfüűT7ż[Ä Ă#}A F^-IrFä v,{5)…(ĺhBËőlśEWB®UF…´ź“"˝DĐŞ'iÔÁ\(+—‰’óÖ˛zčI„aß)Ě­qćpÖq„´¸×ňŐĐ`2gwŘŽFĺQ×xíđ*÷~9F7€ó¶ëpCn%‘{ůvrśa´®Uoo—(4f›ó‘Śž§q*{pNkKč=_G€® ™Łę?„˛`Ř{µH3$k:OŹÁ—UŘäN9l®Šn橥#QM‘“Đr Bń°ZÖÓm_?n©áĹŢťvmQphśfÔż2ÚaĽ©Š†¬~®-ú…а ŤŻhxĘ]Á­Î¬Í9˙ŔnÎ7ú˙¸˙Ä~ú?˙Y¸DtŘ‘a§/âËisZ×Z#EiRU˝“NŮyoög_đ_”oô˙q˙?†sţ÷S˙ý#¤KVú†ż`ú)Eć>P:ÓŤ¶yäž ŕ,ÚéÜ&ş13kę±ôä¦PD„y­:ś ě őŇD3—i\ăł#č+BŠšÄUeŠĚšm(”ÔkŠpřŞŔđśťçŢ eř«X†şŢů´wB»H‚śňĄé…y7VÓý Yďś°q> LÉ<8q#ÍĘnëéήJͶ=‘Ä „ ÉľváW:]L:K-xßŃ@ż8Ű”«$t'B‰ulę$lĺT[˛i×ţ%ôä!UĄë=±ë”M»iü`ÇKÂ~ Ă¦ß˛K@K ˘řĹ`4ĐÚ„ˇÖt íHXáÉäŃŻă+-—«¦§!Ç Ű—­ ]h+ŔŹ6ŃŢĹôë˙‘ń˙F˙÷˙#ßé˙'˙„@T'ńˇC'ůŢŻ[yčäÝŘMÍ6«Zb0MŚőđďÍÖŰŔ z}¬®qŤg/z’ó¤ÉßpeÉęÄkÜđť$[ŤVăLDŢę¦%zŻ+ĚTy´ę’şÚŮŘtÍ=ČĘ5/S6¦Ttĺ ÷x8oźl`čúÁ'O9)˙MĚ‚´<Ń޲žz€@"…Ę5ĽŐ#G$5yXĄ=—6geŰA!ˇ­Ë6ő÷N<H7v=+ OđŤű3ŻůüN˙˙ţÍ˙÷ŰůüOÚŹü˙ćú˙&ţn˙—żů˙púďř˙áóŃßń˙˙Š˙_řűď€˙ ˙'ţ§˙ţń˙}„ßĹ˙/ä˙üń˙}†_ă˙ńţř˙>Âďâ˙§ő˙ţf˙÷§˙ďGMĎďé†mŮB1Ф&{ǹ˨¬$GîWU23m_»xa ÍA¦ĹnšÝݱ™ľ–eÓŔXnçŰb~şC;’Ôä¤hńhZf#Ęâ—×qĎÎQ<ŕîř[ĺC&ąĄá źŰş˘|_Ś}Ýş×k¶‡"űjtDa٬9Ç‹Î4ąŁ Ďşdf ř6ĺţpÜŐ™›ŮýŞv aĘ•jěܬ;dÉ a‹RVt#@‚öV»¶"–Ť7Ü8n|çý™¦">NÄűI~Ň $*|Ę/K3; 6Il¶!¨‡xńĘ^ ´NJ-‹Ś9%/)G‚Ôsť4a€9ôÚƤ‘×üÚŕLJáFE«j\kî)0б6#»ľć,bX8íëíČë-^yÚ Ĺ%G„–HÎ\±Ŕ&«‘S⾤P¬‰Í­–u=_ÂöNjr2ٲrqęaGgű?­gżŃ˙źŕ˙úµţĂŹţ? Ó yzÄîďögRvŘ ¶9C¦©aY7ď?]˙ůF˙÷!Ä7őß~üĘDÖ‘Ŕ让)¨řĄŠŚ8YѶu^f„] t}|äűx¸Ô"v »Ŕ®îô'J(ď|׋† {Řaˇ}?Ś–Ćf•“°ő” 8íŢkR!ÉŽ»Ĺ¨ĘŇFŐCBU¬R"4rđÂKon©Ą×VÜrqú•¦Ť pŮékAo(ł±đ â1ž:ÍŐBĚĹ—`™7s(¤h=˝ëłU=ee}Ť1(Ęć–] q˘ëtR ‘Ü^słif]?†ÖżóŤţ?î˙BŕoîüÔýcĺŘUÜ*´ť¦,éĂěŮwqüÜhČávd¦ßCoXŻ ĆŤYĺs‰*Ck‹Qx“E&.YĽ ‘]ÖČał§x{UP‡•©7ŰÉĽ­Ň­ÎÍ őŢÝĚ‚Î3™ë‰ˇÍŤyG"`–›· ĎDőŢ’˦M”q8čň"qd„‹]2é5ěť(łâkfš×v: LK,ňkú÷?Ě7ú˙¸˙ ężţYŘ›´kĘÉŤjů{2łóXť uŃ2É]ě×Oč8búöń 8N»§‹N,kó»Ö·C_CaéCŻś^R„µ{7:‰µú˛â|éáśsř+ůe ĹČ5‘´J4”˛‘ŘI[é˘$~¤–=ŚĎąšq9K nË‘Ěé­łÔtúżĺmĺĄĎ‡¸, ž6iKă$si_4˝%Eç|űqó;çZ8đŐ! âÄXĐą»Vs¶¤Ć˝ąćQĚÍWMoA뎕µulkĄ¶®Ő¤.]ąXwä™u$żR6‡ÖÓ®dŮÄFë†:ŮěbĹłśář‚ ‚„xB|¤â˙]-íoô˙y˙Jüę˙řÉ˙ÁM&|áaÍoUÝéDóÄ&tŃĐoG]Bř‡§ śjo"ËO•›—Šňʦ8]ŤŚ.OżN$ÎFÇĘvgLâűm(±Ayš Ľ0ÇD   řgEÚű|yR’¨= ¶ű-gĽ¬µŇ·6š t–Qm›F˝5 \ßďv:Ô!Ţ©ËŢë[Ł[p%JĂUc„•Ľ1Ş„cEŮĘŐY>ŻńhČb3łM}¦ÖÚ»"RöĚ×·˘h-dlAŁŽ©óš«t/<źĘ{9÷ťÖš'Öo=pP5b‚ ™q÷\ž˘ âµ…®iDČŽU®Üź·^QŤŁY  P¸Z㪄ŤđĚßşnRŻ®/ýČćŔĆHš/ ČźőŢ#Rµą^Nľ>˝yan©|«ŐŚ‰ÄąUE ső§ŁU«Ř⍎ř?qBóŤţ?î˙Â=˙ůÉ˙"¶;ňI"ă+Ç©=^Ë;Dy\ďš'đŹqúĹ,—SÁJs6q'‚Cîcż ě·’÷ßČf˘ž„§Q.5Č:úţž–”qôqŤËÄi’qÖÓ‰Q0IťOĄ\cʸó©qP¸Ń6&łGIŹţ° >M/‰ł ví[ H&~ł„;iž‚Ŕ]şżpUš đ5Ą¶ŤRÖűş^FElűÜŹł$sŁ~őĹV/Ę0čôg~íFď]Gň´lA»8ăčM älKäťlŐäV°ˇÎ6bď)¶ĐŠ Źéj°hí±•?ü˛ÝĎç´dĺzť^Ć]™ďĚŤąÚě2­t›Şß>~Ávjcµá×{;ä‰Râák[µX­ŁPvşj$HZ6öřÓ§ ßč˙ăő_qâ×ü˙ě˙„Łłô÷zUbÍä­p+Ľ«pŔ¶Ô™Eî[B ‰źrA˙*˘¸äôVÉĘ}§Xíµ©Á_'6—;ŚQśŘ>öŹ3úp íP˙tD€ôĚľ!öČs˙Ů•s\,€,EyWşóoňyyJˇ#†e]¤},ą–7HcnݱšŻÇ±' "Rp•Ż`CÄ…§’ßllxgŘWŞ,Ţg}ÚőaŽBBö jR!ç3’çTe mI„] LNa nŚŻ]©đ;Ľ&¦žL~ĐÚoĺ–yx4ÍÉĂD`ĹSŔéŃăá˛$š+v_c°ĽXKO[ĹjUťĆ{ČEÍh†ă´\”“–~rËňěť×Ň«H–…ďy<čď˝0âI€đŢ>ýśęę‰č?ôGôtÍ)ŽŞ‹ď$E,­$sł×Nj4şUšžlŞL¸ć®¨đwŇă[Ő.X®Čmš‰5cHéé© ĹKýĂ~,Ě•ög4H±ş4ĐĚŕ«"0„FM´]Jţ˙—ŹoüüüWč›÷çţ˙—ď8Dlbý ˙¦“żďţ‡~ú÷ßůź€O˙˙*¬­ŮܶáÔ[M/¦4Ř»IL.÷´Ä s]‹\‘>2VŞnËĹĹ š†ËőŮ‹†QĂSä‹bŤÝÉJqŕAČlŃ‘ŕIiWw"žU •€Şő•ÝÇ'ŕÎ!LŚą'`póČ$ń_ŮlęµR „XO˝+¶Ă ^Śyř+NýOm*¬ójMÂě&ńŹŹPÜÜb*3Q$Y&Ý©I}Ą¶z®÷¨ŽĎ|öÖ…Ź×tŰ@Ľ°/?Ü,Ô^“aQńÇÖ|:Í)A- şŰČ­ň®ŃśOŞÂض’‘3UÜârbÖżĺő~K­dV16l±Bavš˛ŕĎÖJ‡zu‡ý[˝¦ťŕ YrLOEVżř˝+xc½úP3űÁצy«íq+?ÇƬ ĆőH8d¶l󡲛[AÝ; iQo4ăń+#‰Z ”Ă.;SúŢ ˘î˝îąAx>¸üGÁÝ/ţ˙€üÇďýß_ć?˙ąm s˙˙‹ţřGô˙ĂĐŮ˙ _ô˙¤ţ˙ł˙űŢő˙ţ˙sţë!Ľë˙!óź‘S˙#x×˙Cć?ź÷?»ţ??ú‡ôÇNýŹŕ]˙ź řCúă§ţGđ®?ńúźçżCxןüý‘3˙}ďú_>C˙óü_ô˙Eůozë˙†ł˙ëذľőx?őS„#]‹Y"T “*ň±ˇ\¸Ň 1yZŢ.·đädŐV˝jPŃ^ÜM­‡ŃQ2ů|wü×ä˙ź˙ţ.˙yÎ˙;%¦®®Yv-˘CM'B°lA4*Ś@ËŤ ŢłéämÎÁddTg“¬č'ć'˙KřĆ˙‡çżáoöçü˙c>ÖčX%ŐËŕväôoqQ^^íÓ8®*3Ş) gŢ•[?2‡ęhX5BiN°O"¸ŚÉÁ×irnfuÓŔ4đžĺ¨ă©ˇ=ĚäRł4GÜÄş›râ1Č4Ń÷ë=işs¤ÄŻć˙ž˙ţí˛ßsţĂŻô6’ĂjpóĹpV±‚~ôOŇŢ §“ąËhoyOU2D1Ź}˝ÜLŁ ­©â‡L*ÇÔy?„Ř/ő|žvţ«ńŤ˙ŹĎăo÷˙śóżV! ď;¶h[{0A´ŐóńłBűKYáffóh=ł´•J´AőroOw3•®//äδ]Ś!Á·Á•ŕ|…h@`sµ‹” šÚ ;¬ÂvÍI­*9·ĂÔńÚn—«°_ÇÍó»ŔL·ŹIk™EŹ öŐIźŞ.’šzWŮ>ݢça˛Ý…*…ůÔ5?ď(^;•o+Ľ·+¶He1F@ĂH|Q ó˝{Ďĺ¬Ĺ]żĺËV­ţ yä‹ëLŚ\›;˝G0Ó2)Đ~Y>é®ăę­ÍČŚDµý;"«…8ËŇÇXŻ{€FN Ź@žCÇŮ%ĂÍÄťYđ䑆®ş¦€ e¨á•«‚["żń˙áůoyď˙>ý ¸™HĎö˝Řa i©ćx‘jÜI\ CNҰíźÂ·AášDŚ(łfŐ\@#¨:˙)_©’E Ňľ†‡çŃ MÜU+AµÁ%ť¸Í2ŘRßIx?k˛E  ¸XôżĘµ$ó´˝Ĺtß4łˇś#ČÚ¦Ë&ŢŇDżnűBÄ4oZz_¦¤évµvaZË#ŕ&›ďýž!ńűMÂ2­K‡Üí}tÚβňd(7Öíngľ[ěč8ČŠhú*8ÎÜ×ëyOđuŕn”vX‚~U˙ľń˙ńůoä›ůçý߇P/Ş´G٬řž¦×2‰Ňc’­&†înÎ%8ZÜó®Tlźl™`„`#DF¨â~Đ)@Ăä¤mž©V¨#ŹŔÉRwfŔ\gó«Ţ *0í ®Ů’2<`FâîsÜĂśH€«K 'OYObFťĂL“™'/G-—‚_î+|yâ}"92@ŹŘD©AumoWî‘!¬× w0‡€kćŕ˘ďń]ܦ'š$üNeޱD_mu§ „,ÄU3\oá(âíÝńŃî[ÍĆŐ·»óŕRĂ#ŹÍ5eľc R'uâU 걾\¸šá\n'”ćˇجď-N$)ÜŇnpëVgS)˘P*«C3Ź›"ĎŔç@»—^ČUźGăÎQ 4µ c("óN vśMĘzWf i‹JH8ŻHä}ă˙Ăóßň>˙á·üÇé˙?ź"“đĘÁ®BĚÓÔß|™ –¦cÓĆ|=őiPęň˛Ä’ě#Y`.¨u:t×dqăŔ _x´ß<˝ˇ_hlÂrŮO‹ ÖŁn%ĘpíT-3ĄM%¦µăúDT,ęăĆÉL"L RĹÚüÔ®ŰŐNĐž.ŞÍŁgí ţµĘt4»]۵RĶӣâeuěâéNK’ŕÖŔÔV€bj*k·gr$+W“zI@Z Rę«ű’Ž9ÂPx*ť­Ă˙Bţ ÂĎÁçý?GđŤţżôţźßó_8ň[ţţ“ŢGáÔ˙ő˙ůŤź˙üOňňŹürć?á‹ţź”˙;ó_‡đ®˙‡ä˙N˙»ţ’˙;ó_‡đ®˙‡ä˙Îü×!Ľë˙!ůż3˙uďúHţďĚ»ţź‘˙;ó_Çđ®˙‡ä˙Îü×!|Ń˙Wĺżđ3˙ń«‰B¤ž 55€äJYü˛‘ĚĄÖ®4\ŤLť´q¤ŚcőĆÉ’Řv;Ói:IuvłlQh€űÚQTÄgxÎđQŢH„[]Sý/óbäoÂ˙ÂűźÔżĚ˙űéżç s˙˙‹ţ?˙Á˙OţŻĎ˙čŹbż×˙Ďý˙!|Ń˙“ę˙gý÷Ţő˙ú˙Y˙=„wý?¤ţÖá]˙©˙źőßCx×˙Cę˙çţďŢő˙Śú˙Y˙=†wý?¤ţÖá‹ţż¨ţ‹!ď÷żCĐY˙=¶+iE»¶8FšŞ LHű˛ŘUW)[áň眪[éŮŐKÂťÍ×č^ÍŰMc Ůř•ńĆŚ/íćČDK—8˘f¶ĹaÉYćKöÂóI5HGşäŇ S[ş;e;‘d‰&¨+ó¦ÂăzĎKźR’P|ťŘX^™§A»´  ßQeş„°ŮQ”ŠG5_›B+˝[©ŕ‚ÜsçAR&¤Şwń8·čěŐć‹Đc˘KWׄ`éé/`]łÖ}ťR!Ö#ź˝Séb¶á$Áމtë´ŇŇłZ dtťĺđnßďa—™ź“ő  ¦S˛ Uµ óƲrEÝ–ńffiÁÍŞŇÂ2l‹]㙉«ö×ßîřďůĆ˙‡Ď˙BŃoňż§˙aĽăĎÍ@öĚjÍęąyÁňşn€Ó¸•Î=mމr}í¤gäëf~»iłĐMVĹn`Sę ń5on»,ޞ{ůUň^8Ő¬Ôm»čA‘Ś˝ŔÉ`g YČ·Šż˝ÂçěvX‘ÖFŚaśB.›`7lń5Ě}Ť\ă´aŹVČŰ#míŤW.˝Ä"Đ]:ć1·[ě38/¦ůĄqÁü.00VUÜU®óš›Ű8đŻMüĘ rĹ4Tř&ď{ž/צrHXŠZtŰKŘ`cš,_5¬G•ěg÷ŐC_Sg3KÂěČtvŠ‘çż€Ť˙0ßř˙řů_čűüŹsţĎ1ômő|ěĂŘ@#1KĂjQžy-:´Žw©)‡IŽŮ˛ĺҢDrV©Pşî&;P5r"`lműŐ§ÚšP ‰°4řşs™^ËşR]¬KDŐ äüXLlwĽĎb‘|¶+d $Ô§; ¤ˇ‹ćR¬ąx4^Ü |λ˙fó}ßř˙řů_ßÍ˙<ó˙‡pkj¦çŞ»[I€§Ý[G«Yk©ä/ŰEîpNn«†^~ަU‡çVču†Ő[ôŘ'ňçmŐc—WřQ§Rî6„đ¸"±¨ĺ©1W˝ąMĚĆęÝ$L ·űiëĎŕ˙>˙‹€ßýî˙Ź wAů”f_ ľËĽä3ěňafčšS”‘`öĄd‰ŞËšĐŚ’ýă©-îŃ®YĎ>^,3­)>ĺ&ůq·ď–¬—@~\ äEŻ—ˇE)"éPfc$,: ďđU”…`6q çJµÓ|( ŞHgĎ zXTŁňëťa=+úz<¬JŰ}V#út¸†ďó|W°ţéxÄŠíR?–ţ±®é31E ±Ü.R#ĽĘ’Gd˘ ©‹¶’Z9=ŕÚĹpÂě5U.ť+ć# ĚOošYK¬´Ő>ěYuQńNďĘ…#í)ĆDkOö×Í-Z‰¤1cĂŕY„Ű™mp[Ąk’ĽyŞűÉŮŕ@Ş-…–˘ÂÂ…ĆŞQ•N·ş^†„2|é,µ°ŇD`É8&ČЧ%ĐUk5‚˘^÷XŹAÇĚÁt°FÎôţÄ×듯-żsT";‡·ţÓuő˙>˙ ż©˙!§˙Źĺçr€VGzÄlú?íťÇ’ŁZş…ç< Ţ áA„źá=˙ôťuŞnGVeÝČÓŃ™:]|ši‚‹µýż6G—tŃcÇdW›ÇÄUL¶ÎU<ůt‚‰¤ş“>Ńw±¸U ŞŹťÖ2žyG+MuÄBmLD84P%F˘ozI¶@>O—Śö 5^®řÚŰĽ4"áäâHĽ˘‡„,óV’B¤ů8g!Ü“IOşŐ+7vĎ‚sCl¸äˇÎ\pSé˝Č„ó°bKQŮb1v ·|÷±ÍRzĎ?›ßř˙éů_ŕďňż˙?ĎŞT÷r—IąÝRaĽŤ:\´gěL-řŇqţÓ?ďŕ“ůŤ˙źž˙Ăoóżľĺżţ˙|kŐδq®ÄT‚{ѱ‰:-]=×sňkŞ.ć—QąŃÚB̞ގc„쇶š•,˙2gŻ(ÔŤ1ĂŦté,…ç¤g.—éłQíIíCrZŇUXOôú1ńC4ŮXč :ÂĹ/Ä#Ĺ98<ś'ó˙SýŚ÷üŹBż˙ţ A™QZk͵„X%ĽľKŘ.…¶˛uC `´-d­µĂ?4F!ĺŇ»Aµłť.śu7NĹ^áÖRíŇr’đá;łĽčhQŮq,sņcÇxOÎnę~Ź2˝ä&>BĆP@†‹»¸{ŇkÎŐ•®$0;agÉ7Hó]µ f-~r¦›á›“żČ€Ä÷›K—<đň„Ě“‹şáoí%¬Š>é2qË}âóŽÝbóÎŞmĘět_čUęŮßĐ=Cuh]‘¶·sÉ‚ čl}~ň˙W¨˙ú«ţG ěČ{?é_ćÝmH?üďÎ˙ţ­?Š"ř­ýGŕcţ÷^é˙ńżţüOę˙ľ˝'úň9Î>źô˙JőGý×Sx«˙©˙;ężžÂ[ýżHýßQ˙őŢę˙Eę˙ŽúݧđV˙ŻQ˙wÔ=‡·ú‘úżcü˙Ţę˙Eę˙Žüď§đV˙/’˙wäż<…źô˙‡ę?Áßě˙€ÇúßSŘ.ĎLśÝÖ6Řţű:?ů˙+ě˙ŕßÖ˙q˙–˙÷˝?účźó3¸˙Ł˙?{˙Ó_ë˙/ú#˙§˙‡Gţ™C˙ý?7hóÝţCé˙Q8Î<db&ë®§`Đ2Ôfó"[ą 4zvî† Üôá#ńvŚţyí˙Ď:hńž˙ż}÷‹˙Źüď'1ěŕ*Ů}`Ť†¤ ć2źWÚuŞS/°'Ał€ćIVŞbŘUK? KŔ2Úß–ŤŹ1PrŃEâNuîö`q‚b×ôŻ÷ `ËŞďT_đŔĹ*ĄímíXÖ`ĄÔśK·Ń»šf9ˇ­¶±•űt» a^€ iV5Ëm•Łjśó|Yô™T›{®´H 0Ămid‹eđHÂó-oů ÍX«móYÔ ńYŮWtc0ăîĹPR3TtÉIźÝ$žŤ3 7żMôľ"#Čú|rµöŠě.I˛QŘ^Ö=šJ »+¨–ť8Á8ü~©1kĄ'H yY¶aguŢ® 3·PĎŻÂľT(‡‰ uÇ7ŠřK\ŹîŞU`‘äĄĚĄĽ¤‹2ś<Ú‡V´\:Á ˛ŔPřJş"})ts ęuŠ”şŰ¤ö¶iť“ŔÚ ĽZěPÍ5/›î´äzŻľ’Ű֭帝ô‹pćä*n©ýżm÷k˙ÖE«ďů­˙xń?zř˙¬řČŽ™ÍýŽ9ŽÚTŕhó*`7Ţ­6K••^ÓPaâV±÷džgĎ$r–q,Š˝·©ąĎł®’Č{Ö$`ĚxşŻąáV°L°)Ř=Mg‘w‹ÍÎ… ÷ŐnÜá&I’áJäé"žŞ»Č?TęNŕňCČ›ÉĐľôf§ĺł­˝íaoË Ë8VhGElöâćîđůn[“bö(Áąś÷4Ř>“PTWĄno)NĎŚŁn'©źGř$LâWľk%†tľűÂÝŰb¨ Ěf2¦DřĺFĄ†úŐęvpaACĎŤV’ĆUłś´f¨ĚUNˇś¸Ŕ©ŠZÁŚF¬ŕ¬Ëc*-ÜĂ#ÍŮ~@%Äw3ŁőKłVĺ °[n¤µK §xx¬¦ŕÔŽ)Ą€ükŻý˙áÁ?xĎ˙0ńÖ˙ÇýďĎA۰a¨/"TĂĽ÷Q¬JËŘ#x†Ťbf¦^ľĘľ˝„‘\#Ü1»YĘŰj(˝&g¶ëÉüZ …sĹÜ KÚ@]=Ýz°sŇbăÝT-é…r0k~ŁeŐŔÝéý˙ňŻq)­yŮzű¬‚„Ç\sv ZčĂó#~÷®Ś< óaĎH$±ô1Ba4ż‚ĺf%Ł7A^(8řéiĄ×ІE&°@śj†I©şâá3ŐË·.Ô D™Ł&—>-"őÜ«Ž7Ăm“CWm"«mÎO‡H˘¨ąRp÷—ë”ęË802Ş+đ¶6,§»3"ĺµKň xt@ †Ü1Đ~ń›1í÷ě…¸źľF6ޢô¸ü“Đ©ĺ”(3ŇĆ!»Ó˘âdż%›ěeGj_áľ,Í#bˇĚŰě™ő'D‰jK˙®1äÇţGŢ­ŹźřÂŔďřůô˙GáÓ3ćrW’óŮŇwiYśkŤă2Ăą@iáŃĘnŚűŮKJÜÎ3¨›Ű­Ö†± g &=87u¨,XŐ ˛(5 Čŕ”mHˇˇ^e<ZÓCwŁ'Ę[µ<¨ Ót«nďřÄÔ3ă=™ľ’Hf¦‡đ;ć»i»[řľř$Lf˛caCÚn˙ę$Ń v¤ÖŘ\èbj‚¸íj!ˇîGě,™Q|3)ă)şŰhJ˙´@ÍöFJŹÉÜRřňř¨Ć|˝‘ö– čeHB´ěˇŞi$H _FŽ ‡ňôB/Ő’˘Žń ˇĂľđŰÇ˝&˙éf°ŻRťŃMy{‡.XĐútśĚÎ,Ľ ]ˇŇ@ţer°٦Řń °yU±ŰS án^¸–8,M‹=T—&U {óČA™™µ‹)`†Ń%[c¬`©¸ąGUëÖ®őí42.ÁóyPEŔŔZ’ÎX/[+aöá&$[K]Ä·×)zżźLQł” CţSYK^K±Wň݉2·NgŔ٤ dş 6ńŘź VQĘ(¨Ú>éDäŘËŐo¨ +xIŤŢě«ËjvĹ”@[qA+J饺#c*1žűŕ™@s*ÚĚŰCŢfŮ4[siFW_B Ń ^Äx)™r‡Ţ@ëč¨\K­›ÍPÖžö»đp鬉¸ĄhŃ-ާ˛1Éý2…?0dH®Ůł^LzÓ·° ™€qˇCŤSnwĘEđ%‰ęř ҉ŕĚz|ˇV0­áŻ5ČIŃŚ1•T§„Oh¤‚>î¬1wź §«}XÔe»şŰPv(’ÍnáĹ6ÂČ_}]řqüGß­ŹźĹ˙&˙‡ađ3ţ„´ëöeSö$Ą'wC•¬”ă56Ćä<&: ď®Nž|<ş¤˝8||h©U˛ą`ň·ŐRŽcŤHr]•IÝIMŇOfIŠnÜŇĽ(&ďc˛ŃaoJűĹÚšaŁĽ2L¦ŞÜXűŘť‰ę)ˇ Ú5M”?ę‹pą -ő@|€,r—»ÁžJ,Ęđ űţ"Ná:9~Ln~›¦:’7; ÖÄE˝˛˘/PČ9+qEÚßéžď5lK7p±÷‹Šq;°Ě ŮÚ[łGŹőS*`ŰM.ŕĚ´ĄÖ[©,9mzäűvÂ^®]9ĺ$ŞĄ¶yć×•ŞŻíđŮb} ÄQhĆFJĺHQY;;yú%?(ľpGłńÁ~íÔ¨Á/„çĆŽś> ĽgkFLC”§űú:˝5ěg¶sćm9LÓł~$›ťô×÷ÝŠ"–Mó«˘6DÍ•vG|ڎ[ “Y%•VÝu~Ą€X4ŻÇ[G< Óeř9wćÄ@švŘíĽ,¸›ŢNYž)ň·TŁľ’ąŽŚîŇ]˛Ýî“×M÷Ä$°_qČŰ-ÎÎNCýí·†Ćo‚ú<µˇUJL@ĺMşw’ 5´nŮr¦­Ě×=2µ€Ý4̇¦ÎÖ‚y®ě¨W8+r<ÖÄxň˘xQ|33}hÁ±†‰: ­XĆUtÜ,ő8đ‚;0ć@Ż‚¸×%—SťFă?ŤŰ-8•Ŕ$Śž?¬ ę>Ĺ(C>áóŮ!ú*Aw™˙ęáţý8ţżßîëĎň‡?÷˙ţ$98°‰D“«1§ĎÜ”XĄ-ĄŃ+–ńn””Rň2°|°‘uţŰ9ŕ?[?ö?ţn}üĚ˙0üíţ˙çűßÇH·ĹAKĺ0‡˝áÚ„Fb@cFOűU™ql’3¦c{éf§ÇĂf#ÚăĆ[`€eD ń—«qŽÇᆓ`ŻĚ1ě˘Yí®_şH ű YŻČHPí'Zľ4EÜBŇG@µ{˝†*ŮiĹą/Źď3Ä|ś~ěâÝúřYţGŔČ·ţ˙Ü˙˙I©řy¶ /‡ńC{°ŇM#ăPiZ&\j˛,ś…•÷ś§Ňś@xĺĚËT4b¤ÂŕŮvő:ÔĚ)§ k›ď”6÷ęú¨4ś*şÉń6Ylö.¦ËL!Ű.MFĽ]_-ę–^WF[vɑʀÎBV"9dÇ)زĆ<ęd"ÎÖr–n Ü Ä•-2WPÁvŞ˛§řRc=7XŕĂgjŮKĚDxűéwÇĘę˝mëÝsëylá;fŠ„!žAJľČą‰î‘îˇÝŇW Î(Ôµ›Ż =[%–śŃ"]ß‹B'Ňś˛¸5Ţć3ŤćÖdyľŹÍ}Ź3ČÓé”=ŻâŐöU×NË®…cO8‚Ł)‡î–¬4ńSWëËŤš5Ň̲+ÉpčÔňxu](@Gw7@n # Ť‚䑪ýó֑ϧŁĐ!B»˝T"ăî5ě/CóŻ1ßHę(1 ńŕ^á“Ń…H4I>É&,˝lś"z ˛…ÁUö’ŘESôléTź’s0 uA‚ĄéuM쯇JÄőŘ/ŁR::÷©b&GX‰±‰jp%˘çy;Bźů®S´OUŁ/?L@lří¶R(OwB<»2ĆůÍż¬‡<ŃMĘ "mÉG..Š9P-KĂĘ[ęŮŹ—\4ť=_’Ćxń˘yJĺA^śťýzL×®c˝&žHřś,ÂÜY#%ib´ąy ®ÔíXĄĆ†Ť@:„ <+W\[o5ŰbăvŚfŢ⫇s´Ot±*ë\†v NçĎ Š$ń‰×ěóNŕŇí™¶a{hĄQ ĄFdŠĺž.ÖŤÇöŇ·ň;KFĄiÍtÜk/}řÎ’Wu6JĄĘ3©6Cvvhyö‚\u‰ýŕÄQŞó¦,Š×űärGNÜKôáÔ ANÖ§7ćv˛č —ź:Ť¦Xm˝ Ů‚US7O5ť&~…V`ČńëŐQi•ÜôŻ­žß‹˙mŐ7ď]˙AÄ/Ô˙}ů ţ˝éÝžă»úŹ˙?ć˙ľç˙żČůzţż÷FÄ÷ôÉ˙üß÷ü÷—ůŁ˙Í˙Ź źüżá_ý0gď×ÇĎň˙˙É˙÷úA>Ď˙?DßňÇÂďüĺú_ěłţ÷ô=ţ˙ß|Ö˙~Č˙ĎţţăÓ˙˘ďń˙ŕú…żsţ‡~®˙ˇY9ď +eNťůňI >'Ji…\DŹ™P*VŘ“ułěŽîüq>Ó©÷“@tFäȇŃhYîaÓĘ´Č`ÄKÎŰ.GŤâđZÍbX'é[ĆűSăđýȧ—żvˇQŚŢ¬ů«†kSKŘű‹ ě†müŢą‚;Ą}˛ ´Ś>ŃuŹrçžTÁxI'@ľ‡z›€ůj'´ŃĘZ«Ë[4;Żho×Bť¶xĺň(m{·OĽéř>˘™©oÉł;!9…•ŰMŽYŘÇ™NŐU‹>™ř„Ü…¶sĚŐ·‡‰ćMÜd1°ż­¬´]Š".ľc#qŘń}p]™[Äřń±<P*.˝ÝËř!8°I¨Wä¶ÚäÎ"µ5 Pž Şä«ÎXÖuh»4ą}M6-˝5*±”»dgĂ.Ý X#G7"ß;O+ć…Ó÷ńVGjk°čÁ \!•ŰĺBP‰ęĎ.ŢOV|úc˙˙eý„ţľţóÝ˙ń5(E:+…GMhíÇĄş7§4ŽăŁ>¶čŢá;ď¬ÍZéu÷Ý’Ň8# aŔŃsnlŽ” hMkű2éáä< Ű·Ö]ě)¸é˛ÓU+°RŠYUIl‹x-‚Ôŕe ö$ňRáa˛w&~çÍL®#Sӻ졣‰ň1×±É;wŇ0YźÁ Ř-! ĹĎ%i‰őf»|ĘrăK(IŠΫŻÚDC"mŞ•ž'#1_čČ‚Ě-€sŹ-ÖŹü^ŐÖvŮíÁÓ`WďXÚMÄ—¶ĺĽL ĄóW^®—@ĘsÖ©ŻýJčýIßr±óp݇J™#Ô^@ę謟 «eî@j«I˘¤s¸CĆ{ó˘G„żđ«w»˘Ă«áy['9Y—ÝKKMíˇ™Ôk2ĄěŰP~Ü ‘ç• Ĺ/«š+Ćl-ĺé$ĎÍłŕ~ćnĽe#áłá=Y€ł9NÉŔkŤM ×x±Eçô—S„*Őh'÷Ę|DHĚÇ?b˛jęŻ_oQSDWĹ(Á™Ž»›éÝĂ©yĽë)˝Y“łÍ!{¨¦ĺÍF´bU¤yŃÍT ÷q©0ˇĂ˝ëŤúŢ;„Zi'™=ÎNŤxqC+_’źĚˇţăŹý˙—ő˙€ż8űo˙˙÷Pö{"¨ÚrU—´ĎäÚĄó”4†3†‹{Ř˝éĺĂńŐ–y±ý{˛óą–D¸ŚJŢŐ°;8R€BJZŻ»×ŘüŔ€űý";ĺĄŐ.ËY>QaŃrO„lěÜ“đ±…ŐSµ tc‘ľ=Ĺ×ó™ý˙ÔţŻúĎŹŰ~ĆOn˙¬˙ßZ˙ý§ú˙Źű~Ę·ţ˙­˙ŹNüýĘźĹ˙_^˙¦ţóť˙ű"+V6…<ʉU˘EŘ™ůláyB wuňx‡ Ŕw´Łdö& ˘2ť®¨ y'@@ZužÂÖ&ë ¦c ?ç^𡕑şŹEúÖ˙ůÄţxĽý3űÇ‘ßć°ďó˙_Ddp ]V,˛SyqDÉŮLÔ]GÜlÄÉżvmmŇÎç6Uç®e•!Ź“rĚWż–Ň® »±ě˛ˇâY¨â>j mĺ÷ž8]°Wżv ă“9#x`›ĘË–—ó3 ôĆ ©¤Rf—6°:©¸kŹÔöŐ )y®ČZuâp€ő¦äiB:牗ˇ@ű‚ Ű®ş†ŹwÍÜ÷úKHćáْǢÇ0@Ţ|± ŕ×܆Ý]اňÖ!˝J–9MżÜ’=|:2«@W§' Ru4yR)¦…vn›C†NŇý(;łĺV÷[i:†x˝ők@TY[9mĚŤś¬ż7K_iÚŠř»píV>K“îZŹ˝84Źß9_ KCîÄk}DG3&‰ůh®Ł-3㨏Żq›źŘ˙?oő'öÁŕwü˙»;‚ Bµ y˘Öćć 6zĄATŚ˝Ńw ľYZK <¶#Ű–eŕ;zLęÔĄ†e|4¨ÇíÁ.Ř@ćzjăĹzż rłI!ťŁ9Ýç%Ő;+wůc9yk9¶–RÎB=&—÷ęôńÚŚš§čz•b?hp‡ĽŰňçPNó)îhj*ăŚîđ}DwgĽçĚ„f­ą!!č`zyĹNâ¸Ö5ĽhtŇÓP!ˇ;ŁÓ7 ;Ďť¤łşťç^ ‡-9Tt@Ľµ¨Źc<¤Šw蚯 ĺÎ2n[NŤ’[Żn „ó“¬'ÂKĚŰV„óŤS đ0ĐĽC+˘AU«.«˛ŕˇňWJĎS(7¨Üôѱ)Ż%¦oä“2V>EhÚĎĺ‘­°¸ń\E‘­ß×QD”đĘt™Ë°ÝóĹÝ)ë0y¸âP(—ť}›¤Ĺë…ze Ć˙ř=ÔŤ“ÝUQ\—«\Lz—VŢŮ"|ívomÄ- ya)6V î Gčy‰[ÔÁé›Gúĺ)Ť„z 4´ö®5§Ţ<彍©“Há¬ÝŐŮ4`T˛&Ŕq\Hkó9­n4ŐżŘ<~sŰ7`ôÚ«‰?ľ‡«É­Â0Ue MľŞyéWϱ\m–POop˝}ď˙F>ń˙?îÁżňgű?ýÝů?ř—üĎ·˙˙ë)tu vĆš¶!Eń1ĹuHKĘt@éoŰü˙ÎżŘ˙ŹNüţĘżź˙áŹeßůż/ŕý˙’ű˙S˙ďţŹ/âý˙’űż˙…ţßőźżŇ˙Ç7~ü ř?™˙ň=˙çKřý˙ćż|÷˙|-źé˙Ý˙óéşźK˙ďţźß®űąô˙»çż|Ç˙/á3ýżş˙ '>yţŰ÷óż„ětß‘Fůi:é>˝ő&ÓĘKđŃnˇĽ<>đZH™ŘíĎ=Šf¨<1XŇęh›°s ĄŢŐa0‹&ö)‡´¨ ôĎłI¬±'ľźĄĐ‘‘ łŽnčí—ŠĹŁź·úVŚFČ‹E5”"3ŃŽęýľ hő̵_ş— )A€r #ű yüŹ1—: 0Ţoâ|ŁŇ˘oŇNIBUÄŃoG r)Ţ}2bBä¬×B9Ýçvi§Ç–řxČŹ¶ă^ÍŮ˝(…üe&¨u¦‹ĺFdßľ ú.Fô,ÉÜŻŞÍ˛c!“i#|t¤›¸"VNRŃ\¸Łrxę´9Lb‹tÖSy;†îđl&i:+wN‡îq»ÝŇşD0!rT`­§ĹQ>?ŚŞÄ˘Äínšl"řČęŘ.¶˛Ą, ^ÍdĚ)ˇ'›ĄŐŐk¸QĎMÇjQR¤)ÎË8ˇ‹TéÎBíöX×j­vR0żG;­©'tć<ë ąT{…Y˘ń›Ĺąľĺ”“˝mÄ|¬ŕŐ[¨Ő[ÖµqF”`×J‡Ă/´G2ŕB%^wÖ”vIë‘®ö„íŃÍ˝‰vĆÝG侮ŞkŕDľOÜÔÖ–¶‚qO$wCĺTńą9OúŽÜŘŤÚNŘ«ŞM;@üŠW!ŻUy‰ž$˛*ĄaK-Őt T‹…0]¸¬č?ŰŘŇëĄ7R@ŠD˝˘S‚ÎŮYㇼËRä›WQU®c ‘‘ëÍŃČP«×n÷â`łO8gB4@€­§©5Ďţ~ČílÔâÉ“nęĚĐEB#ĂŞňťđmŻŁsĹf3w¨yfę}>ÜWvŐq˘®,·żĘ“ĽILş°}…'÷‹łRp€âđšä!o°ťŤ ’%¸µp(i|Éçmç$Ť|*FŤŕ‚Íűj”dsě-¦á&ëĐpôşszÇÝ Ş„ď?"9˙Çţ˙ ç˙@źĚ˙ř®˙| !_ ]Eý+&D“;š^«ÁG3âÓ°uDwěÚŃńĚj%»†A°]Wt Źí}ďSÓagliďíj0¸nHޱĎ͸̕\Ě1ŞŚÁ7ńZyŕDĂKł$ҧn»ÄK\ÎÔ.)@feÇÉ@A°ł–Ąs|µA3Ç;˛)@Óˇ”ěĂmĂ·Cđ^"^ą Î}´żů­»¶3îÚ^2[cę4–˛‡ wpźÝ)„Ĺ™ ׄŁňR÷(d—­x±óôłşţŘţżpţüűůßý?_wĚŹ®ą%Ě­´…ááj+zwb§áš˘a}S˛ĽLO2/üĄŔ6‰•u4ŇNŁŤőĄÂ(ś>¶†ÍĐ!5{a•\"ÖôŘ”^'IŹ6ĉé€sm)”lĚ{ÇüČä˘`ĂLăĚ8Ő%zá'#_E_ÉTD”vK?Ę2Ĺžsji˛őÖ¶#;|†Ň­.Ř>ăř}'€¬YaăčGfgMÄ2RL˘é3‘%,Đ5¨î«Ăń „ľ•& łCú]@{*‹Zjö„ă ·ŘŞÄŮzK§më•ÜŻ‹®pDŐä~<ř’ZÎ’˝zh¤=â¬Ę–Ř˝‘ÇĆĄl†ĽPk4€Kř}ëÎ醍~:áţŇ-8MŻčMĚŚMÄá…’jĽ¬ĄG¸Š÷ ň źzăá<»Vą 'ňő2ßb!­‡™A›ďJé!{ϲg„†öŽĄ×⇦…´(Ăc[{‰»=D›í·ih­\1ěIŻł6©Ě ÉؿȄ{–ýw˝Bj(ą‹ŕ-ĺĂ˝ň Ŕ­ł¤I—úHěŐĽ/¸ŮČ"ŚB#ă“a6«ŮÁš*XĹť1_. š {ů]÷°:Ř:dmn&ăŔÂt‘ř~ZŻŤłhĐr…vŞůâ‚ó˝x^‚Đžž”a«ˇ*fAŕbÁ;ĎťbŘ`¤‰—*Čâ7aáS…W#ŤöŽ˘ ÚUR43âčź§\ąŮCÜ™@K5Ü9E[AP"[ŚÜĐ%Pöş°h×FčÖ»$–7Apoiéi?ń‘ë`JŐrLŚ˙® 1_ű˙ďŰ˙óQű?ű?ţ$°‹?´UI% q°”ÓdđęýôŁĂQ,®Ęą“1»%·că´€÷€S«;‚„6˘5É Xc]”ˇ^Ú…F¤Ďjzy…š-ů°¶eÎŚľ1ŕĂŠ0«rä@ô2ďhgTFl!4śbŠÄ1 Ą,îJAéO?_F±\nĉĎ7Ůt4#»Ď{; ţęţ-ć#Q~ťĄbE¸5Ł:Ë“ąŮťŽ1ăŘ 9Gcŕ,TŠ„0Gľ\Óͦqm7ł˛ Ŕ*4‡‰n®JYĐi"‡‰ëäCP9¦7Đ6ĆV݆ xďE~&îČđŢ:Ăzw)´*[RP V>ĺ\ČP€b­Č§ú¬ˇ €$AÉęĘ•©»ąťdNă<čÍŐѧŹWÔĐ×Řä¬]:@',í®´ůÝ:µÂ+˙ÝÎĺkű˙¶ý? ô«úߏţű8E3“śońĺ(g˘/WâN˝jPĹ_/­ĽUVV0¨¨ŘtÚl|ŘÍnsĹËîvă…Ô{"©Ţ0Ŕâ€]Ô)J[ú×ĚCܰB¬Ă± ·hy†’„n~]€ńT Ž0Ö 8®Ď%L|YŇfˇ#†”T]îV¸ěá+aRůä3I šşˇ)¶žÂGâ Ľ1É+}ŁlÝžˇsML; Äš=ő®ŞSďŁÄV•őůş-ăN’óŽ´ŐáQŕP›e’Đ3°Š\8v4Ađă1lę•%ŻENđë ŮÓÇđçóDVěŔ/P[;ď#› ç0Zžńv`?íUß,&ßáÎ{ö7xÉ© Góô…xîk»Ťő.,CŢ>Ż1„\’nč>s~dY×Ú¶Ď&ęYYăfʧ¦źÍ–«L!Toľlýh—áÂ(ř=\ !áp[{”NI2G±/Ě?q[>˙?żiĽ`î©ď€˝Śí*Yqé‹WěP±%šž{©ÍÖř·6@™ÍBcs8¸^­›˝UĹ‹éR´x¶użf wc†1Ż$î¸ËFĺBWâÝ,<` Ŷ56Édµ„Ť#G‡čuŽX‘Ůf«÷ćđM4hMŐĚ’Ť6—ńP镨ú ák˙˙}űźđů˙Ďü÷oAČąÎŔéČ+HˇT°«ćÁöç›V^7@ö5ľ>˘Ťaa\ťU&CÚ€4K"Ξ6eÇHíĐęC @T!8MŢhq±Ű“j·•{lçÎč(ĘXMv!äűC¦€ČhŞęŤ«@’p7Ę„]\y›AlgĚ' ›ď]!m;µËÉwŞŽËśŘuz‘˘@Z[ä©WÉ75Ü˙fřÚ˙Ř3ţUţŹţ˘ţ ?ţ˙; „Ä@řݶ`~}L$VąČ—I/§aEĺ±ŐĚúťžX€&§<Ëń˘ď3·••/Úó’\ćÁqeۉd]Ş/IóŚvU›s•)ʬkXď0FĘ`)‚Kf±ćôöÄhXc\o]Ŕ™Áq.ěŘîü2"đ v‘ű·†2üޙÛŤfl„$qďÚ$7°{);+YŁĆ0'büýbN~>A‡`/*Ą×ęÄn^jůkę uT`XŚśE2űp>ÂÖ¬<(„CŻlßęŮĘjvŠTÎą`~a–Ib†V2”¬+u·M TF:ČŹřöM‹\gşqŞ.”Ś8’6jbŹfŹ ݧ Ýáë˝2w¸Rf > ĂőĘ$z$ 2r—iüx~+?óµý“Ř3ţ•ýżŇ?ö˙-*~?ĹQdK}7Eg âĂPŔ¨ „ÖęÔBČh)b;‡â+ärkí†G¨„ĆÜŢ„-ŢĽĆÉĆoő^˙ŕ˙†_Ů˙o1˙űźţĎ?®ů+ü?·˙Żů˙“÷?ţcţëvŔ˙_đ˙Űěüé˙ţâkţ˙Řű?˙.˙Đ?ě˙Ź+Dţ ?üÁ˙{ţ˙oĎF~ćż|ţ™˙ßd˙çO˙ç·ŕWü˙ý˙?ýßß‚/ů˙łű˙ě˙[đ%˙v˙?öĂ˙wŕWüs˙˙ zýb˙ßOý÷[‚Őí…gc/ Ú\-ą¦|şäŇ÷¸^üÉ€QKĎ%ńăYď:WPŻ"ŮÖęIµ¶Íű|ČÎu˘©őZšđn›§H¨k—dö˝Ôy7Ć‚ÁúčO’ߊ›Ô.˛äoůŻ×2ó_…Żí˙űöBĐ?ź˙€?ó?ľĄŁŰ=ű…‹¦đrŹŽTE`(§c0 śÍX %Á’ŐzX`§·{' 3+Ą}ŠŇä˝o·eEĆt<áş+/ş*ĎT"=ý¨Ľ1!ˇa@(ť«ĘÖÓëś}‘d…ozőyy_CĹż¦äű„ÚÇwp#%Fç.¬ř5¨QN8:×ÜяޛśW;KöTKÂNSÇďKëű#"Q«9é•›ËŇÝXŠ ś‹:®™J]®ĺťVOĄööÁ0ćŤ5kŰÖây¸.IwčNÓf—ě$C:PKÝ-q>§çÓ Ńáé‹­.7ˇü™¶Dˇđp1Ŕ"Ň ,J7!ťŚ€GpN˘łÇžµU:Ĺ…ň«źűÖBAşE療š?żS¨©u¶ł„´ö}Y„ÝLš »ŁőĘ©ËĐiěWŮrtŠQ” ÖőŰ›yŘ·FŐtBO13b <íěçi™e—Đ@Mʙګ´ÄvÝűú…ëÂmaăvüŤ˝ëÖąéŽ9ź†Ţ„ôŢ{fôŢűűô˙ÝLŔî(’ľ‘°S)A°‰®Şîf†SxAě`Ž…Á{ ©]’gܫւ:[Ś33N¨¦ˇ†Ëž y[–kŻÄzCŔÖ%ReنŚ3˝q改šL®Á‹P3cTĘÍÔ!s®¸Ú ×ű«Ç]đíp†T{•/S($…Ŕ×"ÉŞ@‹ĚźOAżć˙źË˙Ł_˛˙Űů˙źţ?‚h§ >ëći—Ě˨1ĺÔÓX4Ŕú(+"Ç;Ú¬kĘqX»¤Ĺ- Oźę`@/˙ IČěëkҸFöC€âĺdo^€ď=#Ž«YÝY˝_ŮŘŰBô  ő3P^ŤľĹ!<-ÄĆčŃlB×ÁÁ‚„PĐFëšşo‹6‰‚śŔu1ŐOC?l8x^¬{:! Ô9‘ŻQF5«ŤŐ{äŢšrM]Źńxě¦aT÷řů‰ŐÖhm‚…OăŃÁČöUmX_gFEˇŢÝ{%+scînŇŰđĺČëý¨myëŮhŹmcJw÷ęÁ=jJÂ'Ć–l%…ž ÝʦBłčŮŐ=¶*'aŚŠëá.6¸ˇ-Bu˙Ec&“Qd>Ż>č{A±áÝ$äockźü0ý •ç×ü˙sůůţ'ţđ˙O€7Â@ –Ą×ë÷^©wôÓ´ftłÓ„ΦÇË |ZMQp]; ”TF¦WičŇçŘV—yá™VŮŤX‚”žuIʍ¶ňĽŚ Çҵłi‘}ä9;%.?±5ÂMrLĘś rVđTÄ~PÍÎ o•U^9X)1]şa+0ńX°t{öÇ4Ů4`vNDdÇřúL{păű0”3±E©¸b§đ]ő@çčů!e^;/3ą’Őr€$€¸x>`bsXŔdßIĚ1âYkÉřţ¬G>ˇęHAş%¨FŕĆh9[_–ĺ¨Ý[ZH "étUÇ‹xŐ˝Üé8ÔÖXr¦™łŞdFŰkä˙ôFS'¸Ö´źS={:Ç"I_(5Fg<ßlÄÄ…WĚ \%‚<[ą7ńçŤ âWN%°őy˝¬­‹Đ?şÁ¦ńˇÖ sŻ}Ĺl»T=>Uęď: :,öŘ+Ď]XЇRIrB,ˇ×$c<3Dę­2şÖ9™4l&âEňzŽąěĐŹ°Jňüę¬A»Žy¬Ţůz®.?†ß°jĺ(ŕÓč3>ľSGÄ?Ţw$`üu´RڬŽ~ĄóÂEŃŘK”} Ą1„¬ĐLÄ«ĺČeÍ”Č2}žëZ6ÁűmVĚ`˙ŕ–&Ľ#EI'O•]·çâd lÇŕ2y÷ö8GN+»tÂ&bĐrD.©2ÉŘwü~UŔ˘<Ú‰5čcňśS±ś¨[±g{Ň ŕ•|<=ťŤŠÍWGG_GˇUß•e¦iY¬éůL«,ř~‡łµ}S=¦FČ€HSYl«ÔPňuXş •eî8;ÝnŇž7ń›46oř=¤©bŻ˘ŞcąŹĚcĺNĄťU芢ޯg®V |¬ík˘¶<ĽĐŮ>°^(@-Űa~@Ź~Í˙?–˙‡ŔŻŮ˙“˙ý=Đ' ŢçIŽë9şăŐ˛öu‹r‘AĎŤhŤ}KReq9gɵ•ť –Ă„qâU[Í[©Lľ‹`ŰOó©lńąŽ‹;ö™Mnń2Ň=Đ+MXÓ%8ŮQťŻ1ăÖé¸ łá×3 Đä¨h~żÖět‚DeŃÎgÝ…éĆ 5r”ßË" ĚŘW@ÖfOˇdĹ€wÚ”Llˇ/—Ł V0ów–dިŞc* h¬SĺA"u›Ćaş˛ÇĽż™u¨3Ů@ÍŠ–{xW´źý€Á~bTKů;pŐüŢÇöHŞÓО »ŮzŰbµ¬×ĄĐž˛ĺ›,A‡;]Ąd’÷öL1¬OŃö Çëfőजţöv(}LĽ]¨-fć]9µś9ńTQĎŁ6tdŕ6Ŕى —ŃĐľŽUmťě"•ą¨4÷ŕ.4®Ź/Bn"}9yđ ]Ćr¤Y·ŘŁň ł;Câlm F_§’}ŐŔ…»ÔőíĤ3bĺ†R^C_†EW^ĐŻ… ¶~=–QQżmHwŞđ-—)Wiç>>>ŘEłm›śiäa‘´Ľ˝xżă—ťŔ»˘+ ¦eb.1'÷{Űx…ŤŽäÓBÚŃŇP@Î+’«”""%€Ça׳:3Ń#_u€Q\ĺU~Č!TI¶č0Š¦ŢˇťyłÓ˛-+ŮÄ}´LŢUĽ_QâŁ5ť 1žwbuÔŹĺ'~±ű2Ź~5ÔşěÇŚ­čă_Ű] [ĹÁá·© R…§aŤF©¸xÎfĉ.8!®ó4‚éX¸ůÁŻłjc‹Žť ×1Ö[ĂčřľlZ|_Ť™kó;ój1AEÉH¦Ů¤‹ÁíY#†Ł[źmF`ýťÔg´Š,WĐôşôô˝4q¦|…Ă:C·ßVN#•°|gĐÝ̵N÷ćüŐÂŞa%ľž‰ow$˝Ę0É"@%t5 Z‘Ä@_hšôéĘČE›]t…WôŘűŃ‹UA´·ăĘĽ ±âV¬OP.cf5â˙wąü5˙˙X˙˙î˙˙ě˙˙ W+Z:™­ÎťA¶űÄ ŮgĎĘäg®†Íç&1®Ňc; ¨‘Öť@Un0Ś#řŇ hŘNůăM Ň_|ÔÂpĆeÝx8.;_Ř©÷¬‹ĐíÖmÁ’Łp°ĽZ·¤gX÷Řń8Ü@ĂW‹HhvµĺCPŘŃ Eem–ńCŚI •'_»m\ä±Úa2™° ¶CuśÜą'ĂË2zş¬*Ćgä8lP…ĺkď[”ÁČo.Ż73 ńô «Ţzh#EÜĘ"Űü‘F\>?(ćÁ_ćKN“‹¸ub-uJ ĺë}1>v4‘®ÖŹę6Ź W>$ĺ€nŃźutě-,p3Ă@ÄÓ`—w®Ő”żąKŘđú°© Ű…řřčxgşż»ńřŻç˙Ďĺ˙ ôďűż}˙űgţ˙ďăŔź¸\ j”ëJČ9ąé˘Đ¶^tT Ŕ”y¸-ĽčVk’;ÜIŮVr7H×R›N—3äxfĺ!ejUiđč ©x-H*hPÓPY@@8kîj^ó€JĘ(0ëĄ(‰ú~—j4Č:Ľ—w^?ľ$ÍÝŕZq{žŘ­[QIŹ´ĂWÉFTG7Í›6 l"ZŇžDę#_ßĺ–Ţ²VŔ 3ă˝ÍŘLtófśJJI=p¬Žéűsצ ¶ n•ăČ4ÂăĹĘě˶˘eŔrÍ!v·"ďĂ€žČƨ=ęhÔsóŕrĚ|~ľĘ°:15}„ÍĚëkvíAájdMôcą!lŃĘVô r‡¦/ąi©TúRd׉âł‚%JsÉjÎűÍŹeĆĆ1˝TÚÚ•ł|p…Ęžpó;yŽd2ĂvŰFĄá/FÁ·BµtHo¤7/SwßXv±ł«îŞţČGűbüô=ĘYËEěú™žęA—Ŕýo8ćźć˙˙‰üďź˙˙ţ~]˙ß›˙ý/ůż˙9!ú'ü©˙/ę˙[óż†üÉ˙ý~]˙ßš˙…ţĂŢyě:Ž&Yxϧˇ7Kzď=w˝˝}úąL/¦+5Tެič[ Ĺů#Ä8 ˙Wü­üâ˙łř˙V˙/˙˙wđçř˙ŤĆŻ˙üżř?ţŻoáGń˙Gř?ţĎoá§ń˙ÝţßOţ ?Š˙wďţúuüyţç3˙ů-Ř"Ą+Ci…+°čű"śÉ ŃC˝ÂôČp& ˝L[üJ—řBvÜk6Í:ŽAQ™şńé‰-"ä‚h‡w Ü=‚ľ.> CŔ1dbhšĺpL˘Ŕ z} ¤ooFĽ®A@¶‹Íu¤Äb_ś|oéČĽâ~#MĽ)Ę‘ÇÇ\’f·KkLĽ™—3Ő +°‰Y\MÁ0.Ç(zg°–%Š›ŽiÖÁNßoÄŔCqiUÔŞ–‚_ßá4˙Qrs7dfÁQ2˛¨n śŢđtŔË<Ö¶;ĚËşĐkÔ˘z»Îý´›Ĺ®ě˝ßř>±Š?öh‰Ü([ @»}u™'Q{Ş !°FZ»1Ľď–Eľ»&Vöáµ±-Gę)Ęß&%ľ>$ö‹˝xŽoű9óÉpçÄ’#áDú®O‘%(8”ÉK”mŰvlE˛Đt+;Ť]/»CÄŔŐűpϬąŚţpIś<ěQÉ ŕÔŹŔŮťĹ÷z-ź•ŁC0_÷۵O‘sďń 'ý˙ý®ć‡˙-?×˙ďó˙âčć>÷żŚ$ýV Rˇ 1”#4Ľ`í–EĎ$·ŹÇY÷Dô»¸‰Xoă/Q,rcŘÄöYq}űZ-‚mşm[s*“îÔA$ENŘ’µe(´Î c֋ܲ˝áĆŰË«b[\(-ś4Đ–ő¤ FöD#C&bgˇő„¬ä±§j65U}ěuť!­l6ęŇM'ČŻ©đufŁ“hŇłÇC¶·ś%„Ř6W Ô–÷2®4-ö2cÓâa&űe1ŚŰŞ˘â]č:Ĺ[®ţ­lŁ‹ÎĚŘBŞÁ”jUĹ ădëfĂ5€‰J$ˇTD"vËî^N8QâđzžŽmÚŹâa?”·ÉO’Úâhř`xĘđđ{*7\‡9Š´4Îq'˝ďݸ6\‚Ókěl)9˝r»3=jő{6ŘÖ:P\`߉”-ĄÄ‚oűŠÖ˘Űäv‹4•$ĎúrT¨*|˛㲼ërćąă2ĆŇ]![zŚa±­î 9|F zݨ‹Ý“ÁvĎ0˘B1’ZăŰź=7Á&Î'@ŢÝüd/µuŹźŹ|ˇŁÁŕ‡CŐxJ›ŕXŠŁGn®†›µ »Ş}»ĺÍëŠÖ»ON_DÜwp +ľL^2Ę­őôě)Ő&!ß”0ů«Oâźë˙·ůżĘ˙ĎüĎ±]0śfńÇŚ%áý•ótĐBŇŠčöé{EşL‹eë!#äražtbík…%ŚeŽBŞ\ fĎ4rĹ8üů«Ć}ćµđ• v´˛QęśŕjK»ô»šŔĄš[ÄŢ)ţ•=A°Źü%@&4˘Ä‘pT"¨u¦:ĆęÁ€ =˘VŽě§Dý[řyţ›˙óëőźűř3˙ó-T÷Üá °´'z<˝âŮÚĘ9·ąÍŃťŠÇ»łg›„a‚LG)Áĺ ŐĚ|g¡`uÉq—O6;+CÉC ’Ô>zł±hůĘ©…>Đź~ňËĎó˙űö˙~˝ůŮ˙ţ›řŁŐˇ2 DĄŕNű[ż}»}!L¬rŻU#Ä ľó1şQ:bmęař¤ó ?Ď˙oó}•Řg˙ăo"Оx’mꌠĘSU—ç _’Ç2~äÄÚë âOş˙çňóü˙ľýŻń™˙˙]đ­€Ű/ś\ ę2Wtâ)xNŰ-w^é÷6V0.ěÇ\¦(€«x1-E,˝™°ä­•~ń s4”őKé"1«V^őWůďÄŻţňşßÍľeáőŔč$Ŕ‡šŞh/ňžíÓÍË?öĘKםpኀ˘p«É÷(ɱ׎Ŕ3kß©·PEnńü~F%R‘AâkßoúD¶YŇ$˛…7t Ćş{ĄFÇ !'™iMÎRZe8¦ąîFĽ˙V]RUôX"‹’ĺ’ÄqnŹuŞXőšÁżćůNß_™'ók{oE®s3äóBš´ŃËé:Ůd·j„µÄtŞË3Űr.„`h| ż5X·u`ë íu0ú ýRM(.źwAŘ `TÖ~ j@ŔYčs Íăŕ-@ ŹĐŐWd ç›Lmv˘eZiH•@ş9;čqG˝ŽĎ;Z»1,Č)É9™óľű„şn=şŢ †ÓבwRS~¶ę˙?Ě˙ń™˙ü…ü<ţży˙ţń˙|?Ź˙oö`˙š˙˙µé˙‰˙Ďâ˙›÷ż!źýźßÁ˙˙/Ú·ůWýßW»÷ďó˙źç˙XBŔ¤ bťđ4 ­3Ú¨ŚŔőö=H·ú(ôx&䌕¶Šá„mňgtĹĺPȧ2Im%ĐUĺë"iźŮ7ŁußQůÇ#Bě›iĂ-ąZM«§tĐ!'™¤:úlĆ]¸Ű*k‡!cŚKË)J‘DĽ™.3'hĘŘH5ęë¶–u % n–ĺ¤TéęءXč8?I C‘@Vk˛łŤ§ŰłđÄڭضĄr ‚:|Şř;"|§Ôn«áH,ob^*fÚAKqˇ‰é)¦¨¨čkžmÓXěÜź•‚VĂÖLľ„őđŠť™^õHľ:Ăq”8ĺ–T=• W=˘ŹÍ˝ éĆŇÉÝ/KĂ˝c NŽ{ó3!ĘcD‘#Ş 5 Dč,÷ó.ö˝@ŘQ\ö«3„ ¦é)Őd›XA¶‡cd ŠN¤sścĄ^"ń4ZlĎŁ qР𠜆!NlĹ•­yĄČá^ŤştŇ›Vőń ŐůY@Źj˝cîČrĄ–A" žsęń—ĄĹĄJl„tň<Ź2EâđĆDjś­uK4•^}9ŚŔLžĐŔf^ŁX»¸Sn?âTf!Nžš<.ŕ⫺’KčúcH4 űj?=×?Šč˙ß^pý•ţ˙y˙ËG˙ż‹HsαőťçŞŻäTĽ0ŃG6ó{}öí–¤S„ŞĆY˘OĚ÷jfgó Á/Iˇ¬ŁTŞđuq׫´ťč¬*Ů *(ä ě:c+łVôa/ ćÄ~¤__ €yÑ솤}ľ”ŠҡÔĂsäď± łwS©úé(čXdN˛ŹB}PÂZŤ”×ަ„_äěěóFÖĂ›ŰÔ“sŁÍÝ$¶KVľĽKď›ńíu‡wź”XĹ;#vUZ—cĐYjYÇCČNds⨵NµAu¶Ń?¬fX;†Ś¬Srඉ—mÂđ›ĺÇ.Ůý€‹^]ő‡“{ŔB–ş÷ŻX\śAO;·|ϬL﮳fú§`&ÁŠŢëěé·ŃS8Q& [·¤×•\Ŧ’„łrĹ 9ˇ‰šźV¬%mô |kf*ä—Ś‹˛řQ^{ŕeél¤Z[‰aŻ2ś¨F'6pÝ…~ctŐö:ŽQ6¤÷^[*@RúZşĄ\ŹS&ŮôŁ8ŽyŹô}»mepJĺ°pĂ!ĹÝá·–Ň·Rq}rčoQĺ… ĽCBČ]Ç,KŔĄŹžą3a™ZٵýsB|#?Đ˙żý˙–żŇ ů÷űżŘgţë›xFĚ?{ kCAťüwĘšÄa#“sz”™d.Ű`Ž7¶Xąv'ĚŚ&ČköIôn…ÔkË#ÖLXf<˛h"˙ş2Ás>ˇŃEÂť•u–ôSרĂ6µtjężŘ;Ź^Ç‘, ďůkčE.é˝=ąŁ÷Ţó׏ ( ¦™‹ęÉz] ĽO+A$č(NÜ8!Ý}”§Go Ř;Ŕ˝tE¨ń‘É `5\Ű–SíJčeu}ú3 ]D?ůíŠ3=?§? ׳WaPZ١˛Óií¦Pgí-_Y-_ÎĂžť˝o˝y3ď×ŮŘÚ[Ď\ďQr8śńÝ´ î‰á·^¦!ł˝* ’z°§±¦ ÖmFíĽAŢŤ}ńʨ»Ôş%‹ŕčöWĚŃ%ňX®ş3˛Ôś ß­küé‡2ĆÝó\/®}ń­UFÇ%ď~MČÍąđ T›†¶”Zí­ĎÄ w޶’Hˇ^ś)ÁĂfZ^^mŔűnDç˝MŢŔh«î‡ÖĹŹMÍ%!öÁ§‰Ľg!¬ŽĹą2‘R~đtďL•¸âćđ]Ň€-“©dwd‡śVŚiô)TŁWĺDÉbş~( ŤQˇ%F-¬wí©T gč—xŤ!E€”“lĽmŇćNQÇëDJ ěEŽ~ĘŐO:w!TűvR4„Ć­Ęs±?LĘ]Đ(l äiLG׎d m]ţ˘ĎW› yÇ sĎĎŹ”đzL˘ń]Iv?–„˛$ěéSjÂćĎ]TN˝(dËÂVF^Mg>Ű颮 üHMKEYë{SS¸ĘwĚË8ŞľdŃ=ÔŞ©cňˇęŞN´—ÜzÔđ»\$ú*ąHŁ9z5–Čś:Ő&Ł wK†we*¸îO¬5ŕ%VÚA; Gk-©:ľçvAX4:ÂJ©đL9ĹhŤŕ¸öÇŞo ŮáęV)(Ť“żG$6 R#:XFIY3Ş­‹p$çˇ@‘5eŠ{_řÔF>ßnW4Űf"éźŢ”–ďGĐÓ´ĎtŁ”vÔ±|Ȣßg1×—EŁ>OčO ČE^B—ĎŮĽ14¤›…SĹV8¤SľcD0´ 4@ĚJ˘ÄjŻ$Ç@O\©*ĘŕW|‘¶¶ŕÄ kĚ҇őĽ`ůšŹ|/áMˇ âşĹM@^µ0´×„_«4#µČń;,°˙Ăůą˙Yţýŕţ/čŰ˙ż„űgËĄ%Ń}UÝV…ŘĽˇbĽ §7˛zŹŰfNĐ'ÄĘ%ŔËKŤfŕÜô˝$Ą^ůŕÝtüxž>Đ;á=Žüč4ŮË`ąŕőqÁł2ĺfŔŰ©OÉRĘ{.DQ›áü8 b ŻÉk“ dé·ľy3ŮŠĚÚ©Q7÷k7JA]łŮł”­ě@íËH:G8-ĘnܸĚ)×ćŚű-Ë {ˇCÇş™]f·ĺ ·:%%ˇu™ď¦â(ÜvZĆz=}zlţČ/¤"?^ădtV[ NťąĂlű‘˛T&ź'd\ŕîuűcňşJaó ł®Yú­w7Ľi†ąĘy…,á™m ÍrÖL|Ůň‹~ŹmřŻĺ‰a7Ťgťőn-Đ—ÖĚ•=<şn}ţĐZČŁü©Őµ¶<‹ä®‡oK (öaÔÄ­ZڬËf™¦‚Öy¬u,›‡q‰ôwžSţą˙]ţŚß˙ýßĂ+üÓş9‚Î3˛,hŔ1î˝{!W^``RаŽěfNJ`#†pk7Ĺ)\ŮZň¦ks7/üŕťs#QJĽShß<ĺ•MŰľ%Ë“Ü|ŰË-Ć[öłNď˲Ů6¬Ů6ÄŮŘŁź°ó§U>Ŕ Ć…rE Ę×bU‹órëÇĎÇ˙×ĺżaČżź˙ř^˙ű*&4χ©ŮĘŮ\=˛y*´śĎ…šQť˝lŘjwč˛Â·sőď®w—‘E“!JîÓ˘Éô–˛µŤ »`i•:‰ŮŇëµDéů8L(%X'çÁÝ­Ř0­^1:QľóYŁĘÍČ6ĺ…Dŕ:nΧĂé$]ń&ą·©?-îń"ŐŹíôäˇ4µJšx…ő'čÓm’ŤQ$ď~ž)ô„ŘIÄNÁ)óĽźđ ­Bż0Ě24^ ©öą÷oŮI"Š)â?śN%ڍ+3‡Šŕ-ňE÷|„wa9ĹŞ12OnMůT6˛.#7^D†B¨T‘şÁ™Ý»´%,Dă‡[ĄOµš/Âę€ ‚Ýä'ş¸~ «úůř˙˛ü7~ý`ü÷_írÁdŇíÎ}qpÉ:)±¸®m\‹˘w:TZF„,´¤é™˝dÉÍ=ü[%.™—+ÁĚ»a›Ňˇüëݤůł[ä8–VzäaŁv`gÂÁŢÉwQÉŐEĺĹżgĆ#ŤÖ$ 9d>äÂ;ä6¨!l3$ÉĺµŢĽęŃk·QŤcËŽˇUbrŘzďEbó7B%A±MÁĺ\îEł ŚB8?q0RQĐj͡Ł[  ¦ć•74,íŤvĘd şN<ćcÖP-Š´XÝ+™E_Y<5‘ĆV) ]rOçßţýiűĐÜ9Op-Nm„ľ›î‘‹w_śE;đ"=đ˙ű%¸EÇž n&pZŕń h­vN8V »›«]~ţĂDyrW÷‰®ďô‘Úe ¤žî©ě´Dvd—·ý†!Eďđ KsŽ›lľ×¸–ĎâUĺhĎ®†é#Ň˙°w9¶#IrĎÓP‹%µxÔšÜQk­yúÉf€ĆÔ˙hLŁ&«i ·0CĐ<.eQE5Ť>2jOVW˦ke8­šÖæ’yGĹšcmGw˛ŤˇjÍR6bĘg÷$CJÉĆäzTnúÎí·—ŚŚČĂŐTnëŇűĘMfĎ ĹLź¦ĎK7gkźh=‰†ŽÇa0RŢ:*çbŕy#ÂŕË«z‘?Ő’)®6<ü±¦ć(×Ő™äĆťë qŞTĐĄWăşĹT‰†d˝2+Âv@=UJOH_ě°â)Űű˘Eâj׺·î– %áĘ0µóř€qϡIµ  Ťť}6#†nwd8~«©ö”»=’ ÔúĽQ¬ÇÍŕŐWÖ8”–‘xBß:N?ś˙#>ü}˙˙u×®˙;ý‡ŕž˙˙3˙ó{ĐňČâŠMŔMăŐŘTˇp;ĆŐć»ÖÜh7‰8’HŁĺcęÚ)꫱'»},)QžŠ7ĄĹĚ S7YĄî%ČžP§ó­ë”1ŘFN^eZJăŐ\s’Ľ]ź;ý ®7¦÷©Ű‘’m¸b·ÝCGô}r­¨ĆĂ$ßOA–»#ćmďëcPi2Gô­ëî=‰ăz‰-®]5&Ş=äϸyŘ!†…M¤óR ŠâÇrRSá‹U—śKi± ‚Ö|2TK. Oüôä…ítЏłË Ü®“¦Uđ5mËňLĺ‰â·Ô ýez±ŠHiŐÎgyz†×Ż =`źś˛)}ťž [Ëd öçÎŮ·]döRRµtHadqë“ć&ľ…XŃ ¦µe@ ‹w6çű>±pH“~$s¤ą{¸ăćIa¬(]ŰŐ´Ř®înİ;ÂR&DĎŻĂńŠgëŻůČPΨJ@éŢłtéNł‚˝NĂ'ěVÉTQ ¶¦Ła=JĐÍH)ĎJVcH<•‘&z™­g-ăüĽŔę ćŇA‰řwÓ–1“äŁî3’űőŇ~–/ަ(őę‚OŻA€čzq¶Š×ČÇŚÁ¬ĹKŢXĂß)Šrę±űx«ą<žçř÷âiÉ;nm˝X“–1§á Ů ˝[·%˙HÇ˙ÁďůźúËžńďř#ţ<˙ůŃß‚Ű.eÝŔ˘ĄćóđPĽyÓŐĺ*e-o,ú”ŕ*â7~SUĚmâ/,ěsc—Ľ•S1 Ö!.ďÜńH`ČZtš†dT‘JĐYrŁöpŽc6=á¸<ůÓUl đi⎨9*¸ŁŘAŻ@™Zg1Á9Ä _'Цm’ďfčÜeĄ Q2Ş˛¦Ét‚˛=ŕ“÷1<ęĂVawĺ\â‘ ´˝űLłeFÎZđŚ”ˇTŇ«;†é"ł\Ec|ÓüdąZJn\·Ş, …OŇ9łsquhap©[yc(·)„čĂŻ{ßâŘŞ<ȉĽ×-€<ŔŢçâľ_Á” Źź&vçNT?áëŐdôJb;±ŽP]z‘i–ĺö8-r5ćJ“;Ě]Á!řž4f˝ážą§Ĺ®ěŃí˙qn,®ćČä°đÇĹ;F+ě6Ó¶őš#•í,xĂS١DmŹfĄ.- i€řŠś* ™]Ň7 $b|<ĺfG2ZÓŹčN­[­Ž†‚uqo¦Ź‹Ś°ĹĽŇ'¦·ëWÁUNą°®Ô+˛tĄTî›_.§—Ű·~iź­ µAĐOrä wžuĂŞ ŕF-49QfÎČ÷Ţ…u»®đž%ý‹¨á YHY'ŚxE—>䣛•-…¨@§ÎV稍źŐ!=Ť¦‘§Á$9Őkk>kŞě«ż"l˛hć> šČţtv§pĐKU(&ڦ&‘3ę¤퟼‡7Pď.ą5ID(‹çd÷tx‘}ž‹m_Δ†ed˛A{'°Y˘GxďëĎËé|0Ä”=i)˘jcü‘¤ ß­rŢś Á_ZŢAx,ŹÂŔ·7¬Ý¸ňBćëów|›l˘Ď$[s‰agoQÍGĹŹŰ™}R ˙„]čWü˙˙ü/ůďźůŻ˙źř}ý˙ŢůĎ˙2˙őŻ"żÂOý˙Wý˙ÂŕĎüOň?ůĎoÁŻę˙ŹČ˙ýäżľż­˙ßť˙űé˙oÁŻę˙Ýůż/ü9˙ýř˙ďŔ“âĄN 6G;Á [ÄL\Y JĎR˘rQ°~pý}#÷—'˘řˇ#ĄŠyŰ>ş"ćńu+©ç†+Ě?/~Ţ˝„jöť HľŽRSUcîë)(›b+Ś´b {+žü¨!÷¸+FŮ".ËsŰ:› ®ŚK3“L0Ѷž »ĘCX.§¶1)·~+ł¨;‘ö¤ |xoŇAzř ,—P˘±Kşn”·˙ĽEz#ŽăŐ’CrcnŃŁJý}±ć0ŕ¬ĹT(ČD{ ‚••hN KáÝć:ŮxĎ ¶łÖXĽ…Ç#ô˝¸v0„9ĘŕV,:2¸µ‘íc‚M¸Vť<•¦ EŹ;|9u¬ z×­~ÉĘ2ě㬲Że÷MĘ^© [-gŢĎń˘‡Ý}®x˘7úĂeP¨&‡|đ;är˛¨c:égTĹŚÔâć¦6KŞmdŠ–Śj•°—Ăk„r˘pżÍv§¶‹±×âŚf™Ó†Ćuޤ;67ĆWf"îçH¬ýµ·Ąemë9­Q?–śÍÔ8u!mDÎg âĄM€]Č·Ţäłtű&R$IĽçýĽěţďř=˙[ţÂ_ś˙Á?ü˙8<–üęřJŰŃ1Ç.Ůd·d–ŃzK}ĎĽ~°/%kŃňm!j|aHb¬¬OrPŮ»5™V}‹© e‰,‘1}Ř;NËe-ťR€u[).űR8Xň–±6}ć{ů †1“—ö¤ż&“)1PdĐ3zřś„~Č&}”_Ő×T QÍ®•' ˛µňŹ+Bů`2áŞd­¤«!ŤX§ú¨ď§Ř _0*#–nÚřĐ1YÂŮWFte,Y\U\ź@$Z+ó 5ćç|Xč^ä˙Ö úD9HŹ”öÄi 5˝ »_/ĚBIă{E Hvhp|µ _ŮEL.IĂ6˝77ěUąÂoŤÍŞăőŹň†D8Ňsâ$ŹËv•¸ްköĐWIPGä Ä»z‡Š†x¤ş¶ÚŁä@L|-ÖŃđłnćářöYîٵٖĄđ!äŁîě˛@Gk8Üc(h­rŐŘPRHĎ,!«ó]¸E#ŰתÇzj›u\ľ×  >č\‘ÉĘI á6zĐŃę+>ť…62˘¤čC'‰&ťT90;I\ż˙®ů{ţ˙¶ü'ýJ˙˙ś˙} ¨áKŁ»Ťąńx_r‹€ “ľvOŃ÷_¤žď܇Ěy«…>VEľz7Qv濣OqĘśísÓ¬} °j´#b‡ő.é„´áXzú„%8ű—˙ŽĚ@†ągżAźAEUČžőT%>ŞĽ± ł "CÁ*Äzkôö!6ź¨$uÔ=¨:«Ús3,ş·Ây<{^¤Áča&‚đ]”‹$lź [˘Ëeýń,«Ú ťv4lňí2tyF|YȨŻčILŮWÜN#:l¶& 5 Š´ ްRżhâé”h`é92ÓĚÖ¨8d×Ü1~îFv´‰‹Ůp”Ňżż“˙3üľ˙ż/˙‡żČ˙ýü˙ő-pG°ĚŤµâF?w©čÔ\‘kQşÍ˝)?Ě4ßR5jôŁ„¸ ŞK·áďôý†ç†8v§7ö_ě]Ç®íČ‘ÜókčÍ’Ţ{Ď˝÷öëu4éµ´QßÖhn,p‚ÉʬĘĘđŐ1ľ|Ş'Í I~Đ®a÷‘ăĘN˝ĘţżĘ÷żÇďó˙űî˙áđ/ężźűżß‚ôˇxĹGyxŔ'f‚ĽĘůĆđp†Č˘>n%Ňäë÷±y&ĐźŕO3iÍ{Ľ3öv…ɿ˹#ăHňč=/Š‚p;—üřeâş9HŹě‰"€ĎJ §h«Á˝°gÜ[g«ÂîÜh3Mtž"ť4™&ť®8qÇUφúÇo)ćň˘¶>JÜžPM(!T76¦e (čn¨gM¬ş˘1]eR źb"^]F˝dbcbŽ+yč‚h(•ĽPxJýnLŢ``·Jćß8XŐţĚÁ‹±‰T·„ip;ś¶@m×)ľ!äG tĹ÷ÎöČ`¸6/,o/l*`^u‘úćGji&´ßP»Ţ¶—(&Űy› â âą9đ®mć`†a€/%~}ěC…l…rŤţ±ŽzS¬µH…ú}ęň+ţ˙ô öă˙đřýř˙ÉţßÄßâ˙oźG˙żđ˙߉˙źě˙Ťţř|~?ţ˛˙7ü7ţ˙±m?ń˙źř˙Iţß0ú ˙żźó˙o<ô&_©ś21ˇříşľIŁ@Á]î=ⱥö€gL1źµÜýС*4ĘjtőžuE·(ňI§·ĺťvJ›“_d“±sw”nÜ`ň‚x+• ź ˇ " Z1N±Jt %kj±[Y°pí|OÂŹ;ă+ŰĚjZ v毹ŃÉ4±](pŢÓIdw®‰TSéO^T2ęŞz Ö&‡DŔČÔÁQľŠv3BuŇzp$Y“·fÍd°ÝެŰ}¸Š¨b°“OY¬^’Ý:+Pk4łEąú±ÓŐä|t\ŃX7ÎČk *ľöČŤ¨ X?,af”×o¦k•ÚŞápë§Ŕí>ĽÜÜ+˘ŹKh%»‚÷'_I“z˘Ş@9Ęý9%í‘@ ±7WN•ˇ‚ńëyšw&/ó΀WÓ¬Q™vŻĎˇÁâçSz AčMSŞśĐiŠnŠÉţ6¤‡é şŐ˘'Í.ŰĘSuô<Îď­rlp’Ţ߀ؠüě4íqŁŮ«F_Q ˘«śDĐ>ňč­ĂV·;P¦ó‰Š™đ¬üăTĄ#=ÔlĽzßBČçµE—:-N“ËI.eMÓĂROú‚ČŘF3¬FëçäîĄ °nF×#łÔŽOBĄgĚE?tŻq:ňk˝ŕm%đ߲×ňź_č˙wűż-ń˙ŕ˙Ťüč˙w@>şź1Ú˙Źń ţ»˙3Žý#˙öżuŞÓ_Y–źgś,çƧîŠQq„{Łhž5do Ó^Ă”2uĽć>)HĐĚű¨E-آV6LX Ó•]Ç=bnŘC±Hľę ÷dÂO×›žť“¦˛Fy÷h+n¦Ó3üo-#­Ţ(W‹łxy’Š éńŽÖ…+V´\• zv«1L7ÍŮĎ\ĄoÚ—ť;†ýŕ7ľ7mąí¸zD5î ŹäC6l,{źănŘ˝¸Ú4!˲ŢH7ňúŤWÚą3ka^¶fďĆĂś<"˘‚¬»ď.ŢC‘Ćö“SµÇ=GľŽ©\Ť;ŐąŤ0ěĽŐ!°ÉĂÚ`ś‡€ô'Ëĺ)ë˝Fúď:p[B­“´äNřŽňÚ&W0Á}™±3âÁ{,ÖpľPXěL>` vĹKŇ'÷ţoüŞÎµqZcąö#šXP}ů‰ĎRâťÖ™őŘŃx ,«EµlŚy]‚¤ë¬cęúÔȶ’x¦–§łsAôĘfň÷ŚÄ*íwÜN)ˇl*č>’h2¤—[m—ɢb¤¬úpDtŢĚć•íůsă )V'ÎeË+Ć’ĹíC&áîšŰiÂ}›őř]xXb|Vd´ď`"‚Á‡ÁfŤmńrŻ• +¬·ĽÂĚs+ ę ,) ›ń5ňx 1pŽ‹!ďŘyČx678^´WD„ŃžűĚ]&nFŤ!‚‹ KČE)‘Ńĺ8„§HQˬŕŠ5zÔ`TŹjd-ńtZ•(öEî\w8j\î§*}kFC÷ččÉ3ÜÔ¦»XŃ *˛ć"î¨W‹¤Që7ćz5=k¶“°.s`FËţd—«8ÁĆA.Í+?š¨4o?ą.Ü1uĎÁ8DfO„‡ %ŚsOÇžR{ÝIÝë_ßq{z†3‚‘A¨śTnđEż0aV˘-čW.0Ŕ ŕ­ ďE&µţuúú˙‡řţÓó?đďĎ˙ľęěG˙żý>1Ň…^ćř9ł›˛ĹÁmŁ(Tł'áŔ§ §’ +…#,VH%>5äDđŽŐo0ŹłZťxe6Łŕîr¬r°/ˇ\˘OlOЧo†kŞDA2ŰŚšŮ‘éPačuˇ?öŮP€Ż' ą®ěpjÝőÔ2}~X(cR›/&Řĺ«ró-&é)^űm†Ý}Šĺ\j7`IŐŢśÄn6ůâ[A5Ý´10•a$B˛B‚tÖ‚(K*ÂŢeřdµ ŇA ¸Ŕ’ž0đľIÖI ę8±6ć~0̨ÎQÇ,ÚčúąIŘť4Í]¤0 ÜŞ÷ŚH†dËF˝1·-˘•ŇB˝ ¶1Ń@Ědłű`‡úŐ’ŹăDŢÚ^xGo(Ŕ] ©CJ9ÖmhŹ;ŠŻ ŽĆGąňňÍz…ljł;ą˝Y>Š1Žű#+yTíŰ}±Ş+ÂjČĂú{ÁS+ÁÉÓyĹÂŹ˙čůÜÉS9ę:qĺ.?4u­(Hîה٤ÂťR € ßM°m«ĄŻ7×ÓţŽ¤ŮŚ5ý<űqŁkßĹŹ´ąďDŔCÝ$6»đÁĹÄë1ö¨P"±y'ĄőŇâAq.U…C]äaݧL6TőÂW ׾Jş™:岙Ż0\°3QjŁBdTűŃĚ+nŚVY  Óécź\íÂ*Q®±âýô>z ń•řőV?€9%LÂH%Hj“ &ĽXŔś>@Ń»AďçĹÖ”?/KCőU'ş6 v®Äp* 9ĘÜ”ZǦą>†­‰Ţ%_ŘV5îť´–rxsŕÔ‡Iî#Ç2›OD‡|Îvv]#KGAŘÔ™)ą,Ř›}ßXđ¦ľ'ŢP;Đ®&ľ2Sv•ńĄz­Ż:~ˇxĆţDęʦƎ_ÇU|`”XŤÍÓĐ$AĂ–·‘‹±±44Ăë…ŻX¨Vć%ÄăÔGŔ˙ű¶Jˇ˙˙ľÁżĹżŇ ţEţ÷sţű-Đ€˘5˛Ń˝Ď 2Ţ•†ýąŞIą:|ŮmëJÂ2ˇ…ęˍ슩“VöU6“>ÚAńřúÁ*]Ęđ‡ťłSf“ş¬µďëě{XB9§ą$ŽçŚýrk‘aĺL¤ŻDČQ Ô&˛S˝@TŇ.‰Ú:Ý”0úZ•dľ|˛Ź‚Âöć{×|C>ĘŤiH>­l2TD2»ĎŃă=ťFlÉä@Q{ŚěĄ™wNZ jVÁݶ+`Íź›e÷gę)áĆŘĄ€ËŠ>Ú´–ĺĽ^EŠéLŇ 8)o/lĂÂc䎝śŤLňăŃś‚kąç_ÂŰ ŐsŻ9Z·kU¨çnaG;‰)]®ÇCµŁńř"đľ¸K®u:ĂÇÖ-]=©zu°ťŹkÝł1hßp÷3N—që63ÝĆpźţŇŢyěHŠeaxĎÓŕ!–xxÜď!đćé'K]RWw•¦”RfjJß2bÄĎą÷pL˛łż•I†TMŞ?Hb­o®3é­VäńÔ»ÂĂĄ$x©hĽâOe˛Ń™T·Î¦ś±^X[_Ł ٱi0ŠÎČx¦–áŤ$ˇÁp‰UQ l|†µ¤ô’kĺWŰłň°Ô‹×&ÍćtĹôöz;cÓto€n\٦ëĽ3ŹăI Ü ćVM ÂNŘ ; Ú]÷ÔĎ!ŇúpÖ6–sę(E•Óđʼn&ş1çśËó¬I뻦×–«sŇYÉŇĐE“ţ.b5ËN¦Řś#K¦Rđz¸ŹÎQ MxŮe°Y‡qö’Ő‚‹2˘›`LC0™D´ܱrŇ­`˘ŢöŰ tpTŚ8öS<{B҆K î€I&Z8H‹$łOÓ8Ĺű<@˛ÜĽv[ÎËI'Ž&,ÉE>•­{UVä$**äápÍ 0č–Ä4ňĐĐÉçhu=—ňzKübý˙¸ĆĎßůÝůţiţĎk˙˙Uŕxé@\ĐĂŹ$UAł<‘\ŞqŠ!<đއÖ#Q‘MV#¤]JŮF¸›dŰšc3e;DĐĆť-ÂŰBDi~I‹ë™›=,é˘]gŕmCn.'h©ÉÍH´ßç« !ŠuY÷GÝBRCzk/ Ú¸­ř3E&łú\ôçň ˙xż˝ßůĹ~Ž˙#/˙ ›1ż—Č y%†řĚk«Ţy—xi‚gą»S**;Á"%Á ŹŁćU"ی®ÁŚ 1gÖ„Íđ@`/ß~çBSyÔzőµůšS©aÜgč?ęÜ:âČšy(1÷ VM¦Ą/‚đŔ@śˇ´OĂŇPš9ČÂ&B¦+¸ó6€K7wbݰęrĂŰSDĽíhFsHm)á]“M&oŮ»jä*Ýö¶ĎŰ©Ď7Ńnćb$Jo'řűťVKÁܢ6 TI&Ú¦DŮę!ň|sďŚ~Ş(ľ˘¨ę Ëć’ĄÔAW"±Gú‹xü#˝ŢĹ?ü˙Ń…?ßůć‡÷ĺㆼúż~żĐ˙Sň˙Ţ©˙Źő_źűĽô˙·ţź’˙ő^ýżÍ˙}Ő}>żĐ˙Sň?Ţ˝ţcŻúŻŻŕúĘ÷˙wűźxő˙ţ ţˇUôĂ”}ř5ľÝŹ˙ž˙˙·ţř·˙ßvŻü߯á/ý?©đď;żÓ'ţýý}Í˙ý"BĐj@­ŞS€—±Ĺô˘{®ŰS$Љl¶q éĚ<‰q١[ŮŐńĺ"…óeYÇšO‰C˛1%Ôh}°çűŠ^ןÖáĄ)Ť° ḢOšąđ:sxĚýéw (É=!°5TĎŰ´]±pD#ę á2n€E´T2^W}›îb“çdď˘użHÎ3k{ŹĘU.›BCÜů&ÁšóŇÁQ+­ĆŤtŢ˝gpJ]2 ő"î$SÉ`“M«Éí$o¬‚ů@öýŠ5»!  J0ś1,$Ĺś5ńDQXó‚|}śăUs˝MÁř=XF_¸xaŁKŤRiođÖé íĎ'•“Ĺ­6§ëö»č}1®~@8âG˙ÖAë7ţ‡°źú˝ů˙U˙ő%é(\+ą! §a^íTióŠ©ÝŞ\3בMĚ^®}p/HÁ×-âĐâ8’ .\đN{˝Ň=ľ?–šÜbľ%–—Y2ao÷Ä ź¨,»OŃY­łDĎF'•C,XËC<üܨűvÄ–ž{pÝE2 ‰Đú‰jR|µč?*ň92 éZbÖ•Ďs÷RZlĺjVÇĆŰküt÷–Ę­˛4ŽŤ«RËmc8y ]wVĄ˝ěÎÖĎ÷б\nĺâÁCKTĆ î+ťtújNAwŹş­‚D3I71 moĄÖ8fZ3O*k^†4·2u ¤dĽ7l“\(ů°Ş"f‰v{Y;gÎnöUťÓ\ÓUjc Ó×HŇ uuyĐ;ć´ň¸'*Rřµ&«âUâIsŐÎb#)S©sČîMmÜśh®Y1j‘ŃXÝQ&ľgöüśPçÖ˛ŽŽoÍÔĂä„>|ş‘ëŘČĄ!±5›yéŞLaš©A O!QJyCGś˘jąMí^™DpléH ­]yŚaWzś†Ń“]Č6Đ\Q b®ˇlÍ;LĎ€‘e2šÁS’ Áń2Á˝:=`~ď‘ GoR–şZxb*ńN7V :őL͡ú˝ď„ŁyÔÖ§şűt Ë×(­yüa玗Ť4Ăß —*ቋ‡çŃ­ş¦u…ńs-"ňCyĺ’ëđşĹ9ÓbvŽH˛ÝCކX”ZÇŁŔ¤duňyĺµ,ă糏’©m+ŰsJŃŰęH‹9ůáN¬ÄŚÉj˘ć7đľţ˙¨˙gľ˙ß©˙óß^ńź/^ĽxńâĹ‹Źä?čú^–ŕ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree7.tar.gz0000644000000000000000000000026214567004737015404 0ustar00‹—ĆAíŇK‚0…á.Ą;đŢ>čzŚ6† ‚®_@8Đ„DH˙79“¦Ź{Ú—śÓÁ¬JD\JqLMQĆ aĘŁ˘’RđNĽő꼱qÝk=ÝşţX¬5×Ň6í˝>}^—K÷eźů!sîD?ő®ËPÉZß`Q˙š†ţşD˙[xëżľ4mÉ??cśGU…ýÇ!ŤÝdŢ?öísť}(././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree8.tar.gz0000644000000000000000000000022414567004737015403 0ustar00‹wÇAíŇ1Â0 …á%7ŔnÂudčŇVnŕüĐBÇVbČ€ôË["ËÖKµR.'×”t9§%5'YR4Ć5żśŠJÎ1¤ł:Ń !8źÚ®őńëŐĽw“ŤĂřěoűďŠÍs¶C¶üuí˙ŢŰ»’Vßŕ÷ţنŽţŕČ á®pś(././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree9.ini0000644000000000000000000000042014567004737014754 0ustar00; Huge directory containing many files, directories and links. [names] dirprefix = dir fileprefix = file linkprefix = link [sizes] maxdepth = 2 mindirs = 2 maxdirs = 2 minfiles = 2 maxfiles = 2 minlinks = 2 maxlinks = 4 minsize = 0 maxsize = 300 ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/data/tree9.tar.gz0000644000000000000000000000256214567004737015413 0ustar00‹^Â~AíšŰr˘J†sÍSä 64çKP“ ‚ĽCŠ "OżÍd˛kL2{*©Č85ýÝ´Ą–Ýňń·˝VYWQÄţópSP4M>ŤM˘O#ŠÄ·ń;Šá4I`—‡ ÉŰ.ë™ćXűŐăăCYyŃ&ÁĎßUÇ˙ůś—/ň2ţ!Ôßü‡IuQr«Űŕăţ ŕĐ˙\ůżŃmđq˙äÓýŔ[˙ŕ>üĂü•˙8ɢËřŐs<]Š"~ę§đ×ţJ=<r˙r˙Ó1rśn@ls.óLić&ĂŤ…ÎŇĆ—,bky©¦sRŃVr%zÄŃwx¶šôaŢ33—5…ڬŢA»˝ę‰L­DlmLL5L8b]c.Â…©·ĹIäČ a×#*IËK2»|¤! ¸×ô›ůXhł;Ô0ÓM‘Î|G˘ŞcĂţšoh%Ű)ĂIÉ*Ł9Uפ&Â\ÔöýdőŚÎ.Ź)ćk*U&Łóflř…g¶áZĚe9;ËZéń‘MM–“üîK~WĽ“đŐsü*˙FĽÍ?ó?<Ϊࢺ›˛«NÖcŠ‘F’Yęزó"“±!=×&Ţf¶đłýWn WůĎ’<˝Ńď?MÓ8˙Qřĺ©GpŁóČyţßń“ý˙ĂţÁţż|=W@˙Żýă_=Ç'ücvń˙Ľ¬Ż^Î5Đ˙‹˙Ż/üż~¦˙űżpĺ˙žú°˙3oýßI˙ć®ü˙¦ţßSłďMýŹĂúŘą#KŐ†Yă2±–ÝťŠ€Âç‹Xîr{4Wć™–I÷ f[%çCŻrf°î٨y`›’éËď°ŰKl{LhM›Ž^m(t˛÷Ę>yŃrĘYEIË^†mńÝď™6 ±Ĺččţ••÷}đNţď˙]B˙6˙(Ě˙¬zŻjÉŮMÜăîĽ\ZŐQ`l˝ "&Yh˘Ť\ű٧~ź,.'xJ…µ”t’­–Ît6¶»fřŹä*˙÷Ó˙ĂÖ˙CđŽ˙{č˙axńŰöô˙Ć˙=ô˙đ§ó?ě˙ßžwü_=ǧü“°˙?Ďţo´_ť˙ţú˙?¬˙‚¬ž·­ĆjP}‰5ÝxZµ“P®”ŁI8&Óµ[1¦áŕśé,‹#|Ds(X™7H[NÎăq˛cb®@ÓÜ‘ 4ÇŚ[¨6·¬©‡Q´Ăµd$ěx^Ťcvźŕ«"Š›äL á.6(¤â‹ń1U¸E XP ÁŹůżŐFű«üăÔëú˙’ć¨č`z©xp’쬍ö‚Ł—ĺ(–ÝóYŔąeŠĺiŹ3Ç4ĎŹ Î@ÁK•>_ˇÝZŘ™ĺĺuä  T‹ĎąŁ1MUŇK±ÝĘT{dqĺŇ6bů“ľÜŹFĆŮ›Ě(=lÚȶw—ťŮj{QĎ•s9ŇćŕC (żĂµ=Đ2MplşcÚµN)«9«µ×™¶ŇÉ®k·mwŮ›×@O:ÝﲕŻ’÷yiĆ4+!Íwu»8±]‹‘]“˛·•+žóŁÂ˙;?˙‘°ţýßr˙˙°X˙C @ @ |ЍDQĹP././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/test_actions_util.py0000644000000000000000000002243614567004737016433 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests action utility functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/actions/util.py. Code Coverage ============= This module contains individual tests for the public functions and classes implemented in actions/util.py. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a ACTIONSUTILTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import os import tempfile import unittest from CedarBackup3.actions.util import findDailyDirs, writeIndicatorFile from CedarBackup3.extend.encrypt import ENCRYPT_INDICATOR from CedarBackup3.testutil import buildPath, configureLogging, extractTar, findResources, removedir ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "tree1.tar.gz", "tree8.tar.gz", "tree15.tar.gz", "tree17.tar.gz", "tree18.tar.gz", "tree19.tar.gz", "tree20.tar.gz", ] INVALID_PATH = "bogus" # This path name should never exist ####################################################################### # Test Case Classes ####################################################################### ###################### # TestFunctions class ###################### class TestFunctions(unittest.TestCase): """Tests for the various public functions.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) ####################### # Test findDailyDirs() ####################### def testFindDailyDirs_001(self): """ Test with a nonexistent staging directory. """ stagingDir = self.buildPath([INVALID_PATH]) self.assertRaises(ValueError, findDailyDirs, stagingDir, ENCRYPT_INDICATOR) def testFindDailyDirs_002(self): """ Test with an empty staging directory. """ self.extractTar("tree8") stagingDir = self.buildPath(["tree8", "dir001"]) dailyDirs = findDailyDirs(stagingDir, ENCRYPT_INDICATOR) self.assertEqual([], dailyDirs) def testFindDailyDirs_003(self): """ Test with a staging directory containing only files. """ self.extractTar("tree1") stagingDir = self.buildPath(["tree1"]) dailyDirs = findDailyDirs(stagingDir, ENCRYPT_INDICATOR) self.assertEqual([], dailyDirs) def testFindDailyDirs_004(self): """ Test with a staging directory containing only links. """ self.extractTar("tree15") stagingDir = self.buildPath(["tree15", "dir001"]) dailyDirs = findDailyDirs(stagingDir, ENCRYPT_INDICATOR) self.assertEqual([], dailyDirs) def testFindDailyDirs_005(self): """ Test with a valid staging directory, where the daily directories do NOT contain the encrypt indicator. """ self.extractTar("tree17") stagingDir = self.buildPath(["tree17"]) dailyDirs = findDailyDirs(stagingDir, ENCRYPT_INDICATOR) self.assertEqual(6, len(dailyDirs)) self.assertTrue(self.buildPath(["tree17", "2006", "12", "29"]) in dailyDirs) self.assertTrue(self.buildPath(["tree17", "2006", "12", "30"]) in dailyDirs) self.assertTrue(self.buildPath(["tree17", "2006", "12", "31"]) in dailyDirs) self.assertTrue(self.buildPath(["tree17", "2007", "01", "01"]) in dailyDirs) self.assertTrue(self.buildPath(["tree17", "2007", "01", "02"]) in dailyDirs) self.assertTrue(self.buildPath(["tree17", "2007", "01", "03"]) in dailyDirs) def testFindDailyDirs_006(self): """ Test with a valid staging directory, where the daily directories DO contain the encrypt indicator. """ self.extractTar("tree18") stagingDir = self.buildPath(["tree18"]) dailyDirs = findDailyDirs(stagingDir, ENCRYPT_INDICATOR) self.assertEqual([], dailyDirs) def testFindDailyDirs_007(self): """ Test with a valid staging directory, where some daily directories contain the encrypt indicator and others do not. """ self.extractTar("tree19") stagingDir = self.buildPath(["tree19"]) dailyDirs = findDailyDirs(stagingDir, ENCRYPT_INDICATOR) self.assertEqual(3, len(dailyDirs)) self.assertTrue(self.buildPath(["tree19", "2006", "12", "30"]) in dailyDirs) self.assertTrue(self.buildPath(["tree19", "2007", "01", "01"]) in dailyDirs) self.assertTrue(self.buildPath(["tree19", "2007", "01", "03"]) in dailyDirs) def testFindDailyDirs_008(self): """ Test for case where directories other than daily directories contain the encrypt indicator (the indicator should be ignored). """ self.extractTar("tree20") stagingDir = self.buildPath(["tree20"]) dailyDirs = findDailyDirs(stagingDir, ENCRYPT_INDICATOR) self.assertEqual(6, len(dailyDirs)) self.assertTrue(self.buildPath(["tree20", "2006", "12", "29"]) in dailyDirs) self.assertTrue(self.buildPath(["tree20", "2006", "12", "30"]) in dailyDirs) self.assertTrue(self.buildPath(["tree20", "2006", "12", "31"]) in dailyDirs) self.assertTrue(self.buildPath(["tree20", "2007", "01", "01"]) in dailyDirs) self.assertTrue(self.buildPath(["tree20", "2007", "01", "02"]) in dailyDirs) self.assertTrue(self.buildPath(["tree20", "2007", "01", "03"]) in dailyDirs) ############################ # Test writeIndicatorFile() ############################ def testWriteIndicatorFile_001(self): """ Test with a nonexistent staging directory. """ stagingDir = self.buildPath([INVALID_PATH]) self.assertRaises(IOError, writeIndicatorFile, stagingDir, ENCRYPT_INDICATOR, None, None) def testWriteIndicatorFile_002(self): """ Test with a valid staging directory. """ self.extractTar("tree8") stagingDir = self.buildPath(["tree8", "dir001"]) writeIndicatorFile(stagingDir, ENCRYPT_INDICATOR, None, None) self.assertTrue(os.path.exists(self.buildPath(["tree8", "dir001", ENCRYPT_INDICATOR]))) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/test_amazons3.py0000644000000000000000000010771314567004737015473 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2014-2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests amazons3 extension functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/extend/amazons3.py. Code Coverage ============= This module contains individual tests for the the public classes implemented in extend/amazons3.py. There are also tests for some of the private functions. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract a node, build some XML from it, and then feed that XML back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import tempfile import unittest from CedarBackup3.config import ByteQuantity from CedarBackup3.extend.amazons3 import AmazonS3Config, LocalConfig from CedarBackup3.testutil import ( buildPath, configureLogging, extractTar, failUnlessAssignRaises, findResources, platformMacOsX, removedir, ) from CedarBackup3.tools.amazons3 import _buildSourceFiles, _checkSourceFiles from CedarBackup3.util import UNIT_BYTES, UNIT_GBYTES, UNIT_MBYTES from CedarBackup3.xmlutil import createOutputDom, serializeDom ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "amazons3.conf.1", "amazons3.conf.2", "amazons3.conf.3", "tree1.tar.gz", "tree2.tar.gz", "tree4.tar.gz", "tree8.tar.gz", "tree13.tar.gz", "tree15.tar.gz", "tree16.tar.gz", "tree17.tar.gz", "tree18.tar.gz", "tree19.tar.gz", "tree20.tar.gz", ] ####################################################################### # Test Case Classes ####################################################################### ########################## # TestAmazonS3Config class ########################## class TestAmazonS3Config(unittest.TestCase): """Tests for the AmazonS3Config class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = AmazonS3Config() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ amazons3 = AmazonS3Config() self.assertEqual(False, amazons3.warnMidnite) self.assertEqual(None, amazons3.s3Bucket) self.assertEqual(None, amazons3.encryptCommand) self.assertEqual(None, amazons3.fullBackupSizeLimit) self.assertEqual(None, amazons3.incrementalBackupSizeLimit) def testConstructor_002a(self): """ Test constructor with all values filled in, with valid values (integers). """ amazons3 = AmazonS3Config(True, "bucket", "encrypt", 1, 2) self.assertEqual(True, amazons3.warnMidnite) self.assertEqual("bucket", amazons3.s3Bucket) self.assertEqual("encrypt", amazons3.encryptCommand) self.assertEqual(1, amazons3.fullBackupSizeLimit) self.assertEqual(2, amazons3.incrementalBackupSizeLimit) self.assertEqual(ByteQuantity(1, UNIT_BYTES), amazons3.fullBackupSizeLimit) self.assertEqual(ByteQuantity(2, UNIT_BYTES), amazons3.incrementalBackupSizeLimit) def testConstructor_002b(self): """ Test constructor with all values filled in, with valid values (byte quantities). """ amazons3 = AmazonS3Config(True, "bucket", "encrypt", ByteQuantity(1, UNIT_BYTES), ByteQuantity(2, UNIT_BYTES)) self.assertEqual(True, amazons3.warnMidnite) self.assertEqual("bucket", amazons3.s3Bucket) self.assertEqual("encrypt", amazons3.encryptCommand) self.assertEqual(ByteQuantity(1, UNIT_BYTES), amazons3.fullBackupSizeLimit) self.assertEqual(ByteQuantity(2, UNIT_BYTES), amazons3.incrementalBackupSizeLimit) def testConstructor_003(self): """ Test assignment of warnMidnite attribute, valid value (real boolean). """ amazons3 = AmazonS3Config() self.assertEqual(False, amazons3.warnMidnite) amazons3.warnMidnite = True self.assertEqual(True, amazons3.warnMidnite) amazons3.warnMidnite = False self.assertEqual(False, amazons3.warnMidnite) def testConstructor_004(self): """ Test assignment of warnMidnite attribute, valid value (expression). """ amazons3 = AmazonS3Config() self.assertEqual(False, amazons3.warnMidnite) amazons3.warnMidnite = 0 self.assertEqual(False, amazons3.warnMidnite) amazons3.warnMidnite = [] self.assertEqual(False, amazons3.warnMidnite) amazons3.warnMidnite = None self.assertEqual(False, amazons3.warnMidnite) amazons3.warnMidnite = ["a"] self.assertEqual(True, amazons3.warnMidnite) amazons3.warnMidnite = 3 self.assertEqual(True, amazons3.warnMidnite) def testConstructor_005(self): """ Test assignment of s3Bucket attribute, None value. """ amazons3 = AmazonS3Config(s3Bucket="bucket") self.assertEqual("bucket", amazons3.s3Bucket) amazons3.s3Bucket = None self.assertEqual(None, amazons3.s3Bucket) def testConstructor_006(self): """ Test assignment of s3Bucket attribute, valid value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.s3Bucket) amazons3.s3Bucket = "bucket" self.assertEqual("bucket", amazons3.s3Bucket) def testConstructor_007(self): """ Test assignment of s3Bucket attribute, invalid value (empty). """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.s3Bucket) self.failUnlessAssignRaises(ValueError, amazons3, "s3Bucket", "") self.assertEqual(None, amazons3.s3Bucket) def testConstructor_008(self): """ Test assignment of encryptCommand attribute, None value. """ amazons3 = AmazonS3Config(encryptCommand="encrypt") self.assertEqual("encrypt", amazons3.encryptCommand) amazons3.encryptCommand = None self.assertEqual(None, amazons3.encryptCommand) def testConstructor_009(self): """ Test assignment of encryptCommand attribute, valid value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.encryptCommand) amazons3.encryptCommand = "encrypt" self.assertEqual("encrypt", amazons3.encryptCommand) def testConstructor_010(self): """ Test assignment of encryptCommand attribute, invalid value (empty). """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.encryptCommand) self.failUnlessAssignRaises(ValueError, amazons3, "encryptCommand", "") self.assertEqual(None, amazons3.encryptCommand) def testConstructor_011(self): """ Test assignment of fullBackupSizeLimit attribute, None value. """ amazons3 = AmazonS3Config(fullBackupSizeLimit=100) self.assertEqual(100, amazons3.fullBackupSizeLimit) amazons3.fullBackupSizeLimit = None self.assertEqual(None, amazons3.fullBackupSizeLimit) def testConstructor_012a(self): """ Test assignment of fullBackupSizeLimit attribute, valid int value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.fullBackupSizeLimit) amazons3.fullBackupSizeLimit = 15 self.assertEqual(15, amazons3.fullBackupSizeLimit) self.assertEqual(ByteQuantity(15, UNIT_BYTES), amazons3.fullBackupSizeLimit) def testConstructor_012b(self): """ Test assignment of fullBackupSizeLimit attribute, valid long value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.fullBackupSizeLimit) amazons3.fullBackupSizeLimit = 7516192768 self.assertEqual(7516192768, amazons3.fullBackupSizeLimit) self.assertEqual(ByteQuantity(7516192768, UNIT_BYTES), amazons3.fullBackupSizeLimit) def testConstructor_012c(self): """ Test assignment of fullBackupSizeLimit attribute, valid float value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.fullBackupSizeLimit) amazons3.fullBackupSizeLimit = 7516192768.0 self.assertEqual(7516192768.0, amazons3.fullBackupSizeLimit) self.assertEqual(ByteQuantity(7516192768.0, UNIT_BYTES), amazons3.fullBackupSizeLimit) def testConstructor_012d(self): """ Test assignment of fullBackupSizeLimit attribute, valid string value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.fullBackupSizeLimit) amazons3.fullBackupSizeLimit = "7516192768" self.assertEqual(7516192768, amazons3.fullBackupSizeLimit) self.assertEqual(ByteQuantity("7516192768", UNIT_BYTES), amazons3.fullBackupSizeLimit) def testConstructor_012e(self): """ Test assignment of fullBackupSizeLimit attribute, valid byte quantity value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.fullBackupSizeLimit) amazons3.fullBackupSizeLimit = ByteQuantity(2.5, UNIT_GBYTES) self.assertEqual(ByteQuantity(2.5, UNIT_GBYTES), amazons3.fullBackupSizeLimit) self.assertEqual(2684354560.0, amazons3.fullBackupSizeLimit.bytes) def testConstructor_012f(self): """ Test assignment of fullBackupSizeLimit attribute, valid byte quantity value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.fullBackupSizeLimit) amazons3.fullBackupSizeLimit = ByteQuantity(600, UNIT_MBYTES) self.assertEqual(ByteQuantity(600, UNIT_MBYTES), amazons3.fullBackupSizeLimit) self.assertEqual(629145600.0, amazons3.fullBackupSizeLimit.bytes) def testConstructor_013(self): """ Test assignment of fullBackupSizeLimit attribute, invalid value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.fullBackupSizeLimit) self.failUnlessAssignRaises(ValueError, amazons3, "fullBackupSizeLimit", "xxx") self.assertEqual(None, amazons3.fullBackupSizeLimit) def testConstructor_014(self): """ Test assignment of incrementalBackupSizeLimit attribute, None value. """ amazons3 = AmazonS3Config(incrementalBackupSizeLimit=100) self.assertEqual(100, amazons3.incrementalBackupSizeLimit) amazons3.incrementalBackupSizeLimit = None self.assertEqual(None, amazons3.incrementalBackupSizeLimit) def testConstructor_015a(self): """ Test assignment of incrementalBackupSizeLimit attribute, valid int value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.incrementalBackupSizeLimit) amazons3.incrementalBackupSizeLimit = 15 self.assertEqual(15, amazons3.incrementalBackupSizeLimit) self.assertEqual(ByteQuantity(15, UNIT_BYTES), amazons3.incrementalBackupSizeLimit) def testConstructor_015b(self): """ Test assignment of incrementalBackupSizeLimit attribute, valid long value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.incrementalBackupSizeLimit) amazons3.incrementalBackupSizeLimit = 7516192768 self.assertEqual(7516192768, amazons3.incrementalBackupSizeLimit) self.assertEqual(ByteQuantity(7516192768, UNIT_BYTES), amazons3.incrementalBackupSizeLimit) def testConstructor_015c(self): """ Test assignment of incrementalBackupSizeLimit attribute, valid float value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.incrementalBackupSizeLimit) amazons3.incrementalBackupSizeLimit = 7516192768.0 self.assertEqual(7516192768.0, amazons3.incrementalBackupSizeLimit) self.assertEqual(ByteQuantity(7516192768.0, UNIT_BYTES), amazons3.incrementalBackupSizeLimit) def testConstructor_015d(self): """ Test assignment of incrementalBackupSizeLimit attribute, valid string value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.incrementalBackupSizeLimit) amazons3.incrementalBackupSizeLimit = "7516192768" self.assertEqual(7516192768, amazons3.incrementalBackupSizeLimit) self.assertEqual(ByteQuantity("7516192768", UNIT_BYTES), amazons3.incrementalBackupSizeLimit) def testConstructor_015e(self): """ Test assignment of incrementalBackupSizeLimit attribute, valid byte quantity value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.incrementalBackupSizeLimit) amazons3.incrementalBackupSizeLimit = ByteQuantity(2.5, UNIT_GBYTES) self.assertEqual(ByteQuantity(2.5, UNIT_GBYTES), amazons3.incrementalBackupSizeLimit) self.assertEqual(2684354560.0, amazons3.incrementalBackupSizeLimit.bytes) def testConstructor_015f(self): """ Test assignment of incrementalBackupSizeLimit attribute, valid byte quantity value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.incrementalBackupSizeLimit) amazons3.incrementalBackupSizeLimit = ByteQuantity(600, UNIT_MBYTES) self.assertEqual(ByteQuantity(600, UNIT_MBYTES), amazons3.incrementalBackupSizeLimit) self.assertEqual(629145600.0, amazons3.incrementalBackupSizeLimit.bytes) def testConstructor_016(self): """ Test assignment of incrementalBackupSizeLimit attribute, invalid value. """ amazons3 = AmazonS3Config() self.assertEqual(None, amazons3.incrementalBackupSizeLimit) self.failUnlessAssignRaises(ValueError, amazons3, "incrementalBackupSizeLimit", "xxx") self.assertEqual(None, amazons3.incrementalBackupSizeLimit) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ amazons31 = AmazonS3Config() amazons32 = AmazonS3Config() self.assertEqual(amazons31, amazons32) self.assertTrue(amazons31 == amazons32) self.assertTrue(not amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(amazons31 >= amazons32) self.assertTrue(not amazons31 != amazons32) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ amazons31 = AmazonS3Config(True, "bucket", "encrypt", 1, 2) amazons32 = AmazonS3Config(True, "bucket", "encrypt", 1, 2) self.assertEqual(amazons31, amazons32) self.assertTrue(amazons31 == amazons32) self.assertTrue(not amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(amazons31 >= amazons32) self.assertTrue(not amazons31 != amazons32) def testComparison_003(self): """ Test comparison of two differing objects, warnMidnite differs. """ amazons31 = AmazonS3Config(warnMidnite=False) amazons32 = AmazonS3Config(warnMidnite=True) self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) def testComparison_004(self): """ Test comparison of two differing objects, s3Bucket differs (one None). """ amazons31 = AmazonS3Config() amazons32 = AmazonS3Config(s3Bucket="bucket") self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) def testComparison_005(self): """ Test comparison of two differing objects, s3Bucket differs. """ amazons31 = AmazonS3Config(s3Bucket="bucket1") amazons32 = AmazonS3Config(s3Bucket="bucket2") self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) def testComparison_006(self): """ Test comparison of two differing objects, encryptCommand differs (one None). """ amazons31 = AmazonS3Config() amazons32 = AmazonS3Config(encryptCommand="encrypt") self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) def testComparison_007(self): """ Test comparison of two differing objects, encryptCommand differs. """ amazons31 = AmazonS3Config(encryptCommand="encrypt1") amazons32 = AmazonS3Config(encryptCommand="encrypt2") self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) def testComparison_008(self): """ Test comparison of two differing objects, fullBackupSizeLimit differs (one None). """ amazons31 = AmazonS3Config() amazons32 = AmazonS3Config(fullBackupSizeLimit=1) self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) def testComparison_009(self): """ Test comparison of two differing objects, fullBackupSizeLimit differs. """ amazons31 = AmazonS3Config(fullBackupSizeLimit=1) amazons32 = AmazonS3Config(fullBackupSizeLimit=2) self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) def testComparison_010(self): """ Test comparison of two differing objects, incrementalBackupSizeLimit differs (one None). """ amazons31 = AmazonS3Config() amazons32 = AmazonS3Config(incrementalBackupSizeLimit=1) self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) def testComparison_011(self): """ Test comparison of two differing objects, incrementalBackupSizeLimit differs. """ amazons31 = AmazonS3Config(incrementalBackupSizeLimit=1) amazons32 = AmazonS3Config(incrementalBackupSizeLimit=2) self.assertNotEqual(amazons31, amazons32) self.assertTrue(not amazons31 == amazons32) self.assertTrue(amazons31 < amazons32) self.assertTrue(amazons31 <= amazons32) self.assertTrue(not amazons31 > amazons32) self.assertTrue(not amazons31 >= amazons32) self.assertTrue(amazons31 != amazons32) ######################## # TestLocalConfig class ######################## class TestLocalConfig(unittest.TestCase): """Tests for the LocalConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) def validateAddConfig(self, origConfig): """ Validates that document dumped from ``LocalConfig.addConfig`` results in identical object. We dump a document containing just the amazons3 configuration, and then make sure that if we push that document back into the ``LocalConfig`` object, that the resulting object matches the original. The ``self.failUnlessEqual`` method is used for the validation, so if the method call returns normally, everything is OK. Args: origConfig: Original configuration """ (xmlDom, parentNode) = createOutputDom() origConfig.addConfig(xmlDom, parentNode) xmlData = serializeDom(xmlDom) newConfig = LocalConfig(xmlData=xmlData, validate=False) self.assertEqual(origConfig, newConfig) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalConfig() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = LocalConfig(validate=False) self.assertEqual(None, config.amazons3) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = LocalConfig(validate=True) self.assertEqual(None, config.amazons3) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["amazons3.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test assignment of amazons3 attribute, None value. """ config = LocalConfig() config.amazons3 = None self.assertEqual(None, config.amazons3) def testConstructor_005(self): """ Test assignment of amazons3 attribute, valid value. """ config = LocalConfig() config.amazons3 = AmazonS3Config() self.assertEqual(AmazonS3Config(), config.amazons3) def testConstructor_006(self): """ Test assignment of amazons3 attribute, invalid value (not AmazonS3Config). """ config = LocalConfig() self.failUnlessAssignRaises(ValueError, config, "amazons3", "STRING!") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = LocalConfig() config2 = LocalConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = LocalConfig() config1.amazons3 = AmazonS3Config() config2 = LocalConfig() config2.amazons3 = AmazonS3Config() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, amazons3 differs (one None). """ config1 = LocalConfig() config2 = LocalConfig() config2.amazons3 = AmazonS3Config() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, s3Bucket differs. """ config1 = LocalConfig() config1.amazons3 = AmazonS3Config(True, "bucket1", "encrypt", 1, 2) config2 = LocalConfig() config2.amazons3 = AmazonS3Config(True, "bucket2", "encrypt", 1, 2) self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on a None amazons3 section. """ config = LocalConfig() config.amazons3 = None self.assertRaises(ValueError, config.validate) def testValidate_002(self): """ Test validate on an empty amazons3 section. """ config = LocalConfig() config.amazons3 = AmazonS3Config() self.assertRaises(ValueError, config.validate) def testValidate_003(self): """ Test validate on a non-empty amazons3 section with no values filled in. """ config = LocalConfig() config.amazons3 = AmazonS3Config(None) self.assertRaises(ValueError, config.validate) def testValidate_005(self): """ Test validate on a non-empty amazons3 section with valid values filled in. """ config = LocalConfig() config.amazons3 = AmazonS3Config(True, "bucket") config.validate() ############################ # Test parsing of documents ############################ def testParse_001(self): """ Parse empty config document. """ path = self.resources["amazons3.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlPath=path, validate=True) self.assertRaises(ValueError, LocalConfig, xmlData=contents, validate=True) config = LocalConfig(xmlPath=path, validate=False) self.assertEqual(None, config.amazons3) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual(None, config.amazons3) def testParse_002(self): """ Parse config document with filled-in values. """ path = self.resources["amazons3.conf.2"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.amazons3) self.assertEqual(True, config.amazons3.warnMidnite) self.assertEqual("mybucket", config.amazons3.s3Bucket) self.assertEqual("encrypt", config.amazons3.encryptCommand) self.assertEqual(5368709120, config.amazons3.fullBackupSizeLimit) self.assertEqual(2147483648, config.amazons3.incrementalBackupSizeLimit) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.amazons3) self.assertEqual(True, config.amazons3.warnMidnite) self.assertEqual("mybucket", config.amazons3.s3Bucket) self.assertEqual("encrypt", config.amazons3.encryptCommand) self.assertEqual(5368709120, config.amazons3.fullBackupSizeLimit) self.assertEqual(2147483648, config.amazons3.incrementalBackupSizeLimit) def testParse_003(self): """ Parse config document with filled-in values. """ path = self.resources["amazons3.conf.3"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.amazons3) self.assertEqual(True, config.amazons3.warnMidnite) self.assertEqual("mybucket", config.amazons3.s3Bucket) self.assertEqual("encrypt", config.amazons3.encryptCommand) self.assertEqual(ByteQuantity(2.5, UNIT_GBYTES), config.amazons3.fullBackupSizeLimit) self.assertEqual(ByteQuantity(600, UNIT_MBYTES), config.amazons3.incrementalBackupSizeLimit) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.amazons3) self.assertEqual(True, config.amazons3.warnMidnite) self.assertEqual("mybucket", config.amazons3.s3Bucket) self.assertEqual("encrypt", config.amazons3.encryptCommand) self.assertEqual(ByteQuantity(2.5, UNIT_GBYTES), config.amazons3.fullBackupSizeLimit) self.assertEqual(ByteQuantity(600, UNIT_MBYTES), config.amazons3.incrementalBackupSizeLimit) ################### # Test addConfig() ################### def testAddConfig_001(self): """ Test with empty config document. """ amazons3 = AmazonS3Config() config = LocalConfig() config.amazons3 = amazons3 self.validateAddConfig(config) def testAddConfig_002(self): """ Test with values set. """ amazons3 = AmazonS3Config(True, "bucket", "encrypt", 1, 2) config = LocalConfig() config.amazons3 = amazons3 self.validateAddConfig(config) ################# # TestTool class ################# class TestTool(unittest.TestCase): ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) ########################### # Test _checkSourceFiles() ########################### def testCheckSourceFiles_001(self): """ Test _checkSourceFiles() where some files have an invalid encoding. """ if not platformMacOsX(): self.extractTar("tree13") sourceDir = self.buildPath(["tree13"]) sourceFiles = _buildSourceFiles(sourceDir) self.assertRaises(ValueError, _checkSourceFiles, sourceDir=sourceDir, sourceFiles=sourceFiles) def testCheckSourceFiles_002(self): """ Test _checkSourceFiles() where all files have a valid encoding. """ self.extractTar("tree4") sourceDir = self.buildPath(["tree4", "dir006"]) sourceFiles = _buildSourceFiles(sourceDir) _checkSourceFiles(sourceDir=sourceDir, sourceFiles=sourceFiles) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/test_capacity.py0000644000000000000000000010047614567004737015534 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests capacity extension functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/extend/capacity.py. Code Coverage ============= This module contains individual tests for the the public classes implemented in extend/capacity.py. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract a node, build some XML from it, and then feed that XML back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a CAPACITYTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.extend.capacity import ByteQuantity, CapacityConfig, LocalConfig, PercentageQuantity from CedarBackup3.testutil import configureLogging, failUnlessAssignRaises, findResources from CedarBackup3.util import UNIT_BYTES, UNIT_GBYTES, UNIT_KBYTES, UNIT_MBYTES from CedarBackup3.xmlutil import createOutputDom, serializeDom ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "capacity.conf.1", "capacity.conf.2", "capacity.conf.3", "capacity.conf.4", ] ####################################################################### # Test Case Classes ####################################################################### ############################### # TestPercentageQuantity class ############################### class TestPercentageQuantity(unittest.TestCase): """Tests for the PercentageQuantity class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = PercentageQuantity() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ quantity = PercentageQuantity() self.assertEqual(None, quantity.quantity) self.assertEqual(0.0, quantity.percentage) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ quantity = PercentageQuantity("6") self.assertEqual("6", quantity.quantity) self.assertEqual(6.0, quantity.percentage) def testConstructor_003(self): """ Test assignment of quantity attribute, None value. """ quantity = PercentageQuantity(quantity="1.0") self.assertEqual("1.0", quantity.quantity) self.assertEqual(1.0, quantity.percentage) quantity.quantity = None self.assertEqual(None, quantity.quantity) self.assertEqual(0.0, quantity.percentage) def testConstructor_004(self): """ Test assignment of quantity attribute, valid values. """ quantity = PercentageQuantity() self.assertEqual(None, quantity.quantity) self.assertEqual(0.0, quantity.percentage) quantity.quantity = "1.0" self.assertEqual("1.0", quantity.quantity) self.assertEqual(1.0, quantity.percentage) quantity.quantity = ".1" self.assertEqual(".1", quantity.quantity) self.assertEqual(0.1, quantity.percentage) quantity.quantity = "12" self.assertEqual("12", quantity.quantity) self.assertEqual(12.0, quantity.percentage) quantity.quantity = "0.5" self.assertEqual("0.5", quantity.quantity) self.assertEqual(0.5, quantity.percentage) quantity.quantity = "0.25E2" self.assertEqual("0.25E2", quantity.quantity) self.assertEqual(0.25e2, quantity.percentage) def testConstructor_005(self): """ Test assignment of quantity attribute, invalid value (empty). """ quantity = PercentageQuantity() self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "") self.assertEqual(None, quantity.quantity) def testConstructor_006(self): """ Test assignment of quantity attribute, invalid value (not a floating point number). """ quantity = PercentageQuantity() self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "blech") self.assertEqual(None, quantity.quantity) def testConstructor_007(self): """ Test assignment of quantity attribute, invalid value (negative number). """ quantity = PercentageQuantity() self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-3") self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-6.8") self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-0.2") self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-.1") self.assertEqual(None, quantity.quantity) def testConstructor_008(self): """ Test assignment of quantity attribute, invalid value (larger than 100%). """ quantity = PercentageQuantity() self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "100.0001") self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "101") self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "1e6") self.assertEqual(None, quantity.quantity) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ quantity1 = PercentageQuantity() quantity2 = PercentageQuantity() self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ quantity1 = PercentageQuantity("12") quantity2 = PercentageQuantity("12") self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_003(self): """ Test comparison of two differing objects, quantity differs (one None). """ quantity1 = PercentageQuantity() quantity2 = PercentageQuantity(quantity="12") self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_004(self): """ Test comparison of two differing objects, quantity differs. """ quantity1 = PercentageQuantity("10") quantity2 = PercentageQuantity("12") self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) ########################## # TestCapacityConfig class ########################## class TestCapacityConfig(unittest.TestCase): """Tests for the CapacityConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = CapacityConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ capacity = CapacityConfig() self.assertEqual(None, capacity.maxPercentage) self.assertEqual(None, capacity.minBytes) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ capacity = CapacityConfig(PercentageQuantity("63.2"), ByteQuantity("2.0", UNIT_KBYTES)) self.assertEqual(PercentageQuantity("63.2"), capacity.maxPercentage) self.assertEqual(ByteQuantity("2.0", UNIT_KBYTES), capacity.minBytes) def testConstructor_003(self): """ Test assignment of maxPercentage attribute, None value. """ capacity = CapacityConfig(maxPercentage=PercentageQuantity("63.2")) self.assertEqual(PercentageQuantity("63.2"), capacity.maxPercentage) capacity.maxPercentage = None self.assertEqual(None, capacity.maxPercentage) def testConstructor_004(self): """ Test assignment of maxPercentage attribute, valid value. """ capacity = CapacityConfig() self.assertEqual(None, capacity.maxPercentage) capacity.maxPercentage = PercentageQuantity("63.2") self.assertEqual(PercentageQuantity("63.2"), capacity.maxPercentage) def testConstructor_005(self): """ Test assignment of maxPercentage attribute, invalid value (empty). """ capacity = CapacityConfig() self.assertEqual(None, capacity.maxPercentage) self.failUnlessAssignRaises(ValueError, capacity, "maxPercentage", "") self.assertEqual(None, capacity.maxPercentage) def testConstructor_006(self): """ Test assignment of maxPercentage attribute, invalid value (not a PercentageQuantity). """ capacity = CapacityConfig() self.assertEqual(None, capacity.maxPercentage) self.failUnlessAssignRaises(ValueError, capacity, "maxPercentage", "1.0 GB") self.assertEqual(None, capacity.maxPercentage) def testConstructor_007(self): """ Test assignment of minBytes attribute, None value. """ capacity = CapacityConfig(minBytes=ByteQuantity("1.00", UNIT_KBYTES)) self.assertEqual(ByteQuantity("1.00", UNIT_KBYTES), capacity.minBytes) capacity.minBytes = None self.assertEqual(None, capacity.minBytes) def testConstructor_008(self): """ Test assignment of minBytes attribute, valid value. """ capacity = CapacityConfig() self.assertEqual(None, capacity.minBytes) capacity.minBytes = ByteQuantity("1.00", UNIT_KBYTES) self.assertEqual(ByteQuantity("1.00", UNIT_KBYTES), capacity.minBytes) def testConstructor_009(self): """ Test assignment of minBytes attribute, invalid value (empty). """ capacity = CapacityConfig() self.assertEqual(None, capacity.minBytes) self.failUnlessAssignRaises(ValueError, capacity, "minBytes", "") self.assertEqual(None, capacity.minBytes) def testConstructor_010(self): """ Test assignment of minBytes attribute, invalid value (not a ByteQuantity). """ capacity = CapacityConfig() self.assertEqual(None, capacity.minBytes) self.failUnlessAssignRaises(ValueError, capacity, "minBytes", 12) self.assertEqual(None, capacity.minBytes) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ capacity1 = CapacityConfig() capacity2 = CapacityConfig() self.assertEqual(capacity1, capacity2) self.assertTrue(capacity1 == capacity2) self.assertTrue(not capacity1 < capacity2) self.assertTrue(capacity1 <= capacity2) self.assertTrue(not capacity1 > capacity2) self.assertTrue(capacity1 >= capacity2) self.assertTrue(not capacity1 != capacity2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ capacity1 = CapacityConfig(PercentageQuantity("63.2"), ByteQuantity("1.00", UNIT_MBYTES)) capacity2 = CapacityConfig(PercentageQuantity("63.2"), ByteQuantity("1.00", UNIT_MBYTES)) self.assertEqual(capacity1, capacity2) self.assertTrue(capacity1 == capacity2) self.assertTrue(not capacity1 < capacity2) self.assertTrue(capacity1 <= capacity2) self.assertTrue(not capacity1 > capacity2) self.assertTrue(capacity1 >= capacity2) self.assertTrue(not capacity1 != capacity2) def testComparison_003(self): """ Test comparison of two differing objects, maxPercentage differs (one None). """ capacity1 = CapacityConfig() capacity2 = CapacityConfig(maxPercentage=PercentageQuantity("63.2")) self.assertNotEqual(capacity1, capacity2) self.assertTrue(not capacity1 == capacity2) self.assertTrue(capacity1 < capacity2) self.assertTrue(capacity1 <= capacity2) self.assertTrue(not capacity1 > capacity2) self.assertTrue(not capacity1 >= capacity2) self.assertTrue(capacity1 != capacity2) def testComparison_004(self): """ Test comparison of two differing objects, maxPercentage differs. """ capacity1 = CapacityConfig(PercentageQuantity("15.0"), ByteQuantity("1.00", UNIT_MBYTES)) capacity2 = CapacityConfig(PercentageQuantity("63.2"), ByteQuantity("1.00", UNIT_MBYTES)) self.assertNotEqual(capacity1, capacity2) self.assertTrue(not capacity1 == capacity2) self.assertTrue(capacity1 < capacity2) self.assertTrue(capacity1 <= capacity2) self.assertTrue(not capacity1 > capacity2) self.assertTrue(not capacity1 >= capacity2) self.assertTrue(capacity1 != capacity2) def testComparison_005(self): """ Test comparison of two differing objects, minBytes differs (one None). """ capacity1 = CapacityConfig() capacity2 = CapacityConfig(minBytes=ByteQuantity("1.00", UNIT_MBYTES)) self.assertNotEqual(capacity1, capacity2) self.assertTrue(not capacity1 == capacity2) self.assertTrue(capacity1 < capacity2) self.assertTrue(capacity1 <= capacity2) self.assertTrue(not capacity1 > capacity2) self.assertTrue(not capacity1 >= capacity2) self.assertTrue(capacity1 != capacity2) def testComparison_006(self): """ Test comparison of two differing objects, minBytes differs. """ capacity1 = CapacityConfig(PercentageQuantity("63.2"), ByteQuantity("0.5", UNIT_MBYTES)) capacity2 = CapacityConfig(PercentageQuantity("63.2"), ByteQuantity("1.00", UNIT_MBYTES)) self.assertNotEqual(capacity1, capacity2) self.assertTrue(not capacity1 == capacity2) self.assertTrue(capacity1 < capacity2) self.assertTrue(capacity1 <= capacity2) self.assertTrue(not capacity1 > capacity2) self.assertTrue(not capacity1 >= capacity2) self.assertTrue(capacity1 != capacity2) ######################## # TestLocalConfig class ######################## class TestLocalConfig(unittest.TestCase): """Tests for the LocalConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################ # Setup methods ################ def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) def validateAddConfig(self, origConfig): """ Validates that document dumped from ``LocalConfig.addConfig`` results in identical object. We dump a document containing just the capacity configuration, and then make sure that if we push that document back into the ``LocalConfig`` object, that the resulting object matches the original. The ``self.failUnlessEqual`` method is used for the validation, so if the method call returns normally, everything is OK. Args: origConfig: Original configuration """ (xmlDom, parentNode) = createOutputDom() origConfig.addConfig(xmlDom, parentNode) xmlData = serializeDom(xmlDom) newConfig = LocalConfig(xmlData=xmlData, validate=False) self.assertEqual(origConfig, newConfig) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalConfig() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = LocalConfig(validate=False) self.assertEqual(None, config.capacity) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = LocalConfig(validate=True) self.assertEqual(None, config.capacity) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["capacity.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test assignment of capacity attribute, None value. """ config = LocalConfig() config.capacity = None self.assertEqual(None, config.capacity) def testConstructor_005(self): """ Test assignment of capacity attribute, valid value. """ config = LocalConfig() config.capacity = CapacityConfig() self.assertEqual(CapacityConfig(), config.capacity) def testConstructor_006(self): """ Test assignment of capacity attribute, invalid value (not CapacityConfig). """ config = LocalConfig() self.failUnlessAssignRaises(ValueError, config, "capacity", "STRING!") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = LocalConfig() config2 = LocalConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = LocalConfig() config1.capacity = CapacityConfig() config2 = LocalConfig() config2.capacity = CapacityConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, capacity differs (one None). """ config1 = LocalConfig() config2 = LocalConfig() config2.capacity = CapacityConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, capacity differs. """ config1 = LocalConfig() config1.capacity = CapacityConfig(minBytes=ByteQuantity("0.1", UNIT_MBYTES)) config2 = LocalConfig() config2.capacity = CapacityConfig(minBytes=ByteQuantity("1.00", UNIT_MBYTES)) self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on a None capacity section. """ config = LocalConfig() config.capacity = None self.assertRaises(ValueError, config.validate) def testValidate_002(self): """ Test validate on an empty capacity section. """ config = LocalConfig() config.capacity = CapacityConfig() self.assertRaises(ValueError, config.validate) def testValidate_003(self): """ Test validate on a non-empty capacity section with no values filled in. """ config = LocalConfig() config.capacity = CapacityConfig(None, None) self.assertRaises(ValueError, config.validate) def testValidate_004(self): """ Test validate on a non-empty capacity section with both max percentage and min bytes filled in. """ config = LocalConfig() config.capacity = CapacityConfig(PercentageQuantity("63.2"), ByteQuantity("1.00", UNIT_MBYTES)) self.assertRaises(ValueError, config.validate) def testValidate_005(self): """ Test validate on a non-empty capacity section with only max percentage filled in. """ config = LocalConfig() config.capacity = CapacityConfig(maxPercentage=PercentageQuantity("63.2")) config.validate() def testValidate_006(self): """ Test validate on a non-empty capacity section with only min bytes filled in. """ config = LocalConfig() config.capacity = CapacityConfig(minBytes=ByteQuantity("1.00", UNIT_MBYTES)) config.validate() ############################ # Test parsing of documents ############################ # Some of the byte-size parsing logic is tested more fully in test_split.py. # I decided not to duplicate it here, since it's shared from config.py. def testParse_001(self): """ Parse empty config document. """ path = self.resources["capacity.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlPath=path, validate=True) self.assertRaises(ValueError, LocalConfig, xmlData=contents, validate=True) config = LocalConfig(xmlPath=path, validate=False) self.assertEqual(None, config.capacity) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual(None, config.capacity) def testParse_002(self): """ Parse config document that configures max percentage. """ path = self.resources["capacity.conf.2"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.capacity) self.assertEqual(PercentageQuantity("63.2"), config.capacity.maxPercentage) self.assertEqual(None, config.capacity.minBytes) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.capacity) self.assertEqual(PercentageQuantity("63.2"), config.capacity.maxPercentage) self.assertEqual(None, config.capacity.minBytes) def testParse_003(self): """ Parse config document that configures min bytes, size in bytes. """ path = self.resources["capacity.conf.3"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.capacity) self.assertEqual(None, config.capacity.maxPercentage) self.assertEqual(ByteQuantity("18", UNIT_BYTES), config.capacity.minBytes) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.capacity) self.assertEqual(None, config.capacity.maxPercentage) self.assertEqual(ByteQuantity("18", UNIT_BYTES), config.capacity.minBytes) def testParse_004(self): """ Parse config document with filled-in values, size in KB. """ path = self.resources["capacity.conf.4"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.capacity) self.assertEqual(None, config.capacity.maxPercentage) self.assertEqual(ByteQuantity("1.25", UNIT_KBYTES), config.capacity.minBytes) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.capacity) self.assertEqual(None, config.capacity.maxPercentage) self.assertEqual(ByteQuantity("1.25", UNIT_KBYTES), config.capacity.minBytes) ################### # Test addConfig() ################### def testAddConfig_001(self): """ Test with empty config document. """ capacity = CapacityConfig() config = LocalConfig() config.capacity = capacity self.validateAddConfig(config) def testAddConfig_002(self): """ Test with max percentage value set. """ capacity = CapacityConfig(maxPercentage=PercentageQuantity("63.29128310980123")) config = LocalConfig() config.capacity = capacity self.validateAddConfig(config) def testAddConfig_003(self): """ Test with min bytes value set, byte values. """ capacity = CapacityConfig(minBytes=ByteQuantity("121231", UNIT_BYTES)) config = LocalConfig() config.capacity = capacity self.validateAddConfig(config) def testAddConfig_004(self): """ Test with min bytes value set, KB values. """ capacity = CapacityConfig(minBytes=ByteQuantity("63352", UNIT_KBYTES)) config = LocalConfig() config.capacity = capacity self.validateAddConfig(config) def testAddConfig_005(self): """ Test with min bytes value set, MB values. """ capacity = CapacityConfig(minBytes=ByteQuantity("63352", UNIT_MBYTES)) config = LocalConfig() config.capacity = capacity self.validateAddConfig(config) def testAddConfig_006(self): """ Test with min bytes value set, GB values. """ capacity = CapacityConfig(minBytes=ByteQuantity("63352", UNIT_GBYTES)) config = LocalConfig() config.capacity = capacity self.validateAddConfig(config) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1529562 cedar_backup3-3.8.1/tests/test_cdwriter.py0000644000000000000000000023512114567004737015556 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests CD writer functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/writers/cdwriter.py. This code was consolidated from writertests.py and imagetests.py at the same time cdwriter.py was created. Code Coverage ============= This module contains individual tests for the public classes implemented in cdwriter.py. Unfortunately, it's rather difficult to test this code in an automated fashion, even if you have access to a physical CD writer drive. It's even more difficult to test it if you are running on some build daemon (think of a Debian autobuilder) which can't be expected to have any hardware or any media that you could write to. Because of this, there aren't any tests below that actually cause CD media to be written to. As a compromise, much of the implementation is in terms of private static methods that have well-defined behaviors. Normally, I prefer to only test the public interface to class, but in this case, testing the private methods will help give us some reasonable confidence in the code, even if we can't write a physical disc or can't run all of the tests. This isn't perfect, but it's better than nothing. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== Some Cedar Backup regression tests require a specialized environment in order to run successfully. This environment won't necessarily be available on every build system out there (for instance, on a Debian autobuilder). Because of this, the default behavior is to run a "reduced feature set" test suite that has no surprising system, kernel or network requirements. There are no special dependencies for these tests. I used to try and run tests against an actual device, to make sure that this worked. However, those tests ended up being kind of bogus, because my main development environment doesn't have a writer, and even if it had one, any device with the same name on another user's system wouldn't necessarily return sensible results. That's just pointless. We'll just have to rely on the other tests to make sure that things seem sensible. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.testutil import configureLogging from CedarBackup3.writers.cdwriter import ( MEDIA_CDR_74, MEDIA_CDR_80, MEDIA_CDRW_74, MEDIA_CDRW_80, CdWriter, MediaCapacity, MediaDefinition, ) ####################################################################### # Module-wide configuration and constants ####################################################################### MB650 = 650.0 * 1024.0 * 1024.0 # 650 MB MB700 = 700.0 * 1024.0 * 1024.0 # 700 MB ILEAD = 11400.0 * 2048.0 # Initial lead-in SLEAD = 6900.0 * 2048.0 # Session lead-in DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "tree9.tar.gz", ] SUDO_CMD = [ "sudo", ] HDIUTIL_CMD = [ "hdiutil", ] INVALID_FILE = "bogus" # This file name should never exist ####################################################################### # Test Case Classes ####################################################################### ############################ # TestMediaDefinition class ############################ class TestMediaDefinition(unittest.TestCase): """Tests for the MediaDefinition class.""" @classmethod def setUpClass(cls): configureLogging() def testConstructor_001(self): """ Test the constructor with an invalid media type. """ self.assertRaises(ValueError, MediaDefinition, 100) def testConstructor_002(self): """ Test the constructor with the ``MEDIA_CDR_74`` media type. """ media = MediaDefinition(MEDIA_CDR_74) self.assertEqual(MEDIA_CDR_74, media.mediaType) self.assertEqual(False, media.rewritable) self.assertNotEqual(0, media.initialLeadIn) # just care that it's set, not what its value is self.assertNotEqual(0, media.leadIn) # just care that it's set, not what its value is self.assertEqual(332800, media.capacity) def testConstructor_003(self): """ Test the constructor with the ``MEDIA_CDRW_74`` media type. """ media = MediaDefinition(MEDIA_CDRW_74) self.assertEqual(MEDIA_CDRW_74, media.mediaType) self.assertEqual(True, media.rewritable) self.assertNotEqual(0, media.initialLeadIn) # just care that it's set, not what its value is self.assertNotEqual(0, media.leadIn) # just care that it's set, not what its value is self.assertEqual(332800, media.capacity) def testConstructor_004(self): """ Test the constructor with the ``MEDIA_CDR_80`` media type. """ media = MediaDefinition(MEDIA_CDR_80) self.assertEqual(MEDIA_CDR_80, media.mediaType) self.assertEqual(False, media.rewritable) self.assertNotEqual(0, media.initialLeadIn) # just care that it's set, not what its value is self.assertNotEqual(0, media.leadIn) # just care that it's set, not what its value is self.assertEqual(358400, media.capacity) def testConstructor_005(self): """ Test the constructor with the ``MEDIA_CDRW_80`` media type. """ media = MediaDefinition(MEDIA_CDRW_80) self.assertEqual(MEDIA_CDRW_80, media.mediaType) self.assertEqual(True, media.rewritable) self.assertNotEqual(0, media.initialLeadIn) # just care that it's set, not what its value is self.assertNotEqual(0, media.leadIn) # just care that it's set, not what its value is self.assertEqual(358400, media.capacity) ############################ # TestMediaCapacity class ############################ class TestMediaCapacity(unittest.TestCase): """Tests for the MediaCapacity class.""" @classmethod def setUpClass(cls): configureLogging() def testConstructor_001(self): """ Test the constructor. """ capacity = MediaCapacity(100, 200, (300, 400)) self.assertEqual(100, capacity.bytesUsed) self.assertEqual(200, capacity.bytesAvailable) self.assertEqual((300, 400), capacity.boundaries) ##################### # TestCdWriter class ##################### class TestCdWriter(unittest.TestCase): """Tests for the CdWriter class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ################### # Test constructor ################### def testConstructor_001(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid non-ATA SCSI id and defaults for the remaining arguments. Make sure that ``unittest=True`` """ writer = CdWriter(device="/dev/null", scsiId="0,0,0", unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("0,0,0", writer.scsiId) self.assertEqual("0,0,0", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_74, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_002(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid ATA SCSI id and defaults for the remaining arguments. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId="ATA:0,0,0", unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("ATA:0,0,0", writer.scsiId) self.assertEqual("ATA:0,0,0", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_74, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_003(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid ATAPI SCSI id and defaults for the remaining arguments. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId="ATAPI:0,0,0", unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("ATAPI:0,0,0", writer.scsiId) self.assertEqual("ATAPI:0,0,0", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_74, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_004(self): """ Test the constructor with device ``/dev/null`` (which is writable and exists). Use an invalid SCSI id and defaults for the remaining arguments. Make sure that ``unittest=False``. """ self.assertRaises(ValueError, CdWriter, device="/dev/null", scsiId="blech", unittest=False) def testConstructor_005(self): """ Test the constructor with device ``/dev/null`` (which is writable and exists). Use an invalid SCSI id and defaults for the remaining arguments. Make sure that ``unittest=True``. """ self.assertRaises(ValueError, CdWriter, device="/dev/null", scsiId="blech", unittest=True) def testConstructor_006(self): """ Test the constructor with a non-absolute device path. Use a valid SCSI id and defaults for the remaining arguments. Make sure that ``unittest=False``. """ self.assertRaises(ValueError, CdWriter, device="dev/null", scsiId="0,0,0", unittest=False) def testConstructor_007(self): """ Test the constructor with a non-absolute device path. Use a valid SCSI id and defaults for the remaining arguments. Make sure that ``unittest=True``. """ self.assertRaises(ValueError, CdWriter, device="dev/null", scsiId="0,0,0", unittest=True) def testConstructor_008(self): """ Test the constructor with an absolute device path that does not exist. Use a valid SCSI id and defaults for the remaining arguments. Make sure that ``unittest=False``. """ self.assertRaises(ValueError, CdWriter, device="/bogus", scsiId="0,0,0", unittest=False) def testConstructor_009(self): """ Test the constructor with an absolute device path that does not exist. Use a valid SCSI id and defaults for the remaining arguments. Make sure that ``unittest=True``. """ writer = CdWriter(device="/bogus", scsiId="0,0,0", unittest=True) self.assertEqual("/bogus", writer.device) self.assertEqual("0,0,0", writer.scsiId) self.assertEqual("0,0,0", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_74, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_010(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and a value of 0 for the drive speed. Make sure that ``unittest=False``. """ self.assertRaises(ValueError, CdWriter, device="/dev/null", scsiId="0,0,0", driveSpeed=0, unittest=False) def testConstructor_011(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and a value of 0 for the drive speed. Make sure that ``unittest=True``. """ self.assertRaises(ValueError, CdWriter, device="/dev/null", scsiId="0,0,0", driveSpeed=0, unittest=True) def testConstructor_012(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and a value of 1 for the drive speed. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId="0,0,0", driveSpeed=1, unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("0,0,0", writer.scsiId) self.assertEqual("0,0,0", writer.hardwareId) self.assertEqual(1, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_74, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_013(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and a value of 5 for the drive speed. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId="0,0,0", driveSpeed=5, unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("0,0,0", writer.scsiId) self.assertEqual("0,0,0", writer.hardwareId) self.assertEqual(5, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_74, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_014(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and an invalid media type. Make sure that ``unittest=False``. """ self.assertRaises(ValueError, CdWriter, device="/dev/null", scsiId="0,0,0", mediaType=42, unittest=False) def testConstructor_015(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and an invalid media type. Make sure that ``unittest=True``. """ self.assertRaises(ValueError, CdWriter, device="/dev/null", scsiId="0,0,0", mediaType=42, unittest=True) def testConstructor_016(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and a media type of MEDIA_CDR_74. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId="0,0,0", mediaType=MEDIA_CDR_74, unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("0,0,0", writer.scsiId) self.assertEqual("0,0,0", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDR_74, writer.media.mediaType) self.assertEqual(False, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_017(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and a media type of MEDIA_CDRW_74. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId="0,0,0", mediaType=MEDIA_CDRW_74, unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("0,0,0", writer.scsiId) self.assertEqual("0,0,0", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_74, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_018(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and a media type of MEDIA_CDR_80. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId="0,0,0", mediaType=MEDIA_CDR_80, unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("0,0,0", writer.scsiId) self.assertEqual("0,0,0", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDR_80, writer.media.mediaType) self.assertEqual(False, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_019(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use a valid SCSI id and a media type of MEDIA_CDRW_80. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId="0,0,0", mediaType=MEDIA_CDRW_80, unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual("0,0,0", writer.scsiId) self.assertEqual("0,0,0", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_80, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_020(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use None for SCSI id and a media type of MEDIA_CDRW_80. Make sure that ``unittest=True``. """ writer = CdWriter(device="/dev/null", scsiId=None, mediaType=MEDIA_CDRW_80, unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual(None, writer.scsiId) self.assertEqual("/dev/null", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_80, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(False, writer._noEject) def testConstructor_021(self): """ Test the constructor with device ``/dev/null``, which is writable and exists. Use None for SCSI id and a media type of MEDIA_CDRW_80. Make sure that ``unittest=True``. Use ``noEject=True``. """ writer = CdWriter(device="/dev/null", scsiId=None, mediaType=MEDIA_CDRW_80, noEject=True, unittest=True) self.assertEqual("/dev/null", writer.device) self.assertEqual(None, writer.scsiId) self.assertEqual("/dev/null", writer.hardwareId) self.assertEqual(None, writer.driveSpeed) self.assertEqual(MEDIA_CDRW_80, writer.media.mediaType) self.assertEqual(True, writer.isRewritable()) self.assertEqual(True, writer._noEject) #################################### # Test the capacity-related methods #################################### def testCapacity_001(self): """ Test _calculateCapacity for boundaries of None and MEDIA_CDR_74. """ expectedAvailable = MB650 - ILEAD # 650 MB, minus initial lead-in media = MediaDefinition(MEDIA_CDR_74) boundaries = None capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(0, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual(None, capacity.boundaries) def testCapacity_002(self): """ Test _calculateCapacity for boundaries of None and MEDIA_CDRW_74. """ expectedAvailable = MB650 - ILEAD # 650 MB, minus initial lead-in media = MediaDefinition(MEDIA_CDRW_74) boundaries = None capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(0, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual(None, capacity.boundaries) def testCapacity_003(self): """ Test _calculateCapacity for boundaries of None and MEDIA_CDR_80. """ expectedAvailable = MB700 - ILEAD # 700 MB, minus initial lead-in media = MediaDefinition(MEDIA_CDR_80) boundaries = None capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(0, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual(None, capacity.boundaries) def testCapacity_004(self): """ Test _calculateCapacity for boundaries of None and MEDIA_CDRW_80. """ expectedAvailable = MB700 - ILEAD # 700 MB, minus initial lead-in media = MediaDefinition(MEDIA_CDRW_80) boundaries = None capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(0, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual(None, capacity.boundaries) def testCapacity_005(self): """ Test _calculateCapacity for boundaries of (0, 1) and MEDIA_CDR_74. """ expectedUsed = 1 * 2048.0 # 1 sector expectedAvailable = MB650 - SLEAD - expectedUsed # 650 MB, minus session lead-in, minus 1 sector media = MediaDefinition(MEDIA_CDR_74) boundaries = (0, 1) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((0, 1), capacity.boundaries) def testCapacity_006(self): """ Test _calculateCapacity for boundaries of (0, 1) and MEDIA_CDRW_74. """ expectedUsed = 1 * 2048.0 # 1 sector expectedAvailable = MB650 - SLEAD - expectedUsed # 650 MB, minus session lead-in, minus 1 sector media = MediaDefinition(MEDIA_CDRW_74) boundaries = (0, 1) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((0, 1), capacity.boundaries) def testCapacity_007(self): """ Test _calculateCapacity for boundaries of (0, 1) and MEDIA_CDR_80. """ expectedUsed = 1 * 2048.0 # 1 sector expectedAvailable = MB700 - SLEAD - expectedUsed # 700 MB, minus session lead-in, minus 1 sector media = MediaDefinition(MEDIA_CDR_80) boundaries = (0, 1) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) # 700 MB - lead-in - 1 sector self.assertEqual((0, 1), capacity.boundaries) def testCapacity_008(self): """ Test _calculateCapacity for boundaries of (0, 1) and MEDIA_CDRW_80. """ expectedUsed = 1 * 2048.0 # 1 sector expectedAvailable = MB700 - SLEAD - expectedUsed # 700 MB, minus session lead-in, minus 1 sector media = MediaDefinition(MEDIA_CDRW_80) boundaries = (0, 1) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((0, 1), capacity.boundaries) def testCapacity_009(self): """ Test _calculateCapacity for boundaries of (0, 999) and MEDIA_CDR_74. """ expectedUsed = 999 * 2048.0 # 999 sectors expectedAvailable = MB650 - SLEAD - expectedUsed # 650 MB, minus session lead-in, minus 999 sectors media = MediaDefinition(MEDIA_CDR_74) boundaries = (0, 999) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((0, 999), capacity.boundaries) def testCapacity_010(self): """ Test _calculateCapacity for boundaries of (0, 999) and MEDIA_CDRW_74. """ expectedUsed = 999 * 2048.0 # 999 sectors expectedAvailable = MB650 - SLEAD - expectedUsed # 650 MB, minus session lead-in, minus 999 sectors media = MediaDefinition(MEDIA_CDRW_74) boundaries = (0, 999) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((0, 999), capacity.boundaries) def testCapacity_011(self): """ Test _calculateCapacity for boundaries of (0, 999) and MEDIA_CDR_80. """ expectedUsed = 999 * 2048.0 # 999 sectors expectedAvailable = MB700 - SLEAD - expectedUsed # 700 MB, minus session lead-in, minus 999 sectors media = MediaDefinition(MEDIA_CDR_80) boundaries = (0, 999) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((0, 999), capacity.boundaries) def testCapacity_012(self): """ Test _calculateCapacity for boundaries of (0, 999) and MEDIA_CDRW_80. """ expectedUsed = 999 * 2048.0 # 999 sectors expectedAvailable = MB700 - SLEAD - expectedUsed # 700 MB, minus session lead-in, minus 999 sectors media = MediaDefinition(MEDIA_CDRW_80) boundaries = (0, 999) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((0, 999), capacity.boundaries) def testCapacity_013(self): """ Test _calculateCapacity for boundaries of (500, 1000) and MEDIA_CDR_74. """ expectedUsed = 1000 * 2048.0 # 1000 sectors expectedAvailable = MB650 - SLEAD - expectedUsed # 650 MB, minus session lead-in, minus 1000 sectors media = MediaDefinition(MEDIA_CDR_74) boundaries = (500, 1000) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((500, 1000), capacity.boundaries) def testCapacity_014(self): """ Test _calculateCapacity for boundaries of (500, 1000) and MEDIA_CDRW_74. """ expectedUsed = 1000 * 2048.0 # 1000 sectors expectedAvailable = MB650 - SLEAD - expectedUsed # 650 MB, minus session lead-in, minus 1000 sectors media = MediaDefinition(MEDIA_CDRW_74) boundaries = (500, 1000) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((500, 1000), capacity.boundaries) def testCapacity_015(self): """ Test _calculateCapacity for boundaries of (500, 1000) and MEDIA_CDR_80. """ expectedUsed = 1000 * 2048.0 # 1000 sectors expectedAvailable = MB700 - SLEAD - expectedUsed # 700 MB, minus session lead-in, minus 1000 sectors media = MediaDefinition(MEDIA_CDR_80) boundaries = (500, 1000) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((500, 1000), capacity.boundaries) def testCapacity_016(self): """ Test _calculateCapacity for boundaries of (500, 1000) and MEDIA_CDRW_80. """ expectedUsed = 1000 * 2048.0 # 1000 sectors expectedAvailable = MB700 - SLEAD - expectedUsed # 700 MB, minus session lead-in, minus 1000 sectors media = MediaDefinition(MEDIA_CDRW_80) boundaries = (500, 1000) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) # 650 MB minus lead-in self.assertEqual((500, 1000), capacity.boundaries) def testCapacity_017(self): """ Test _getBoundaries when self.deviceSupportsMulti is False; entireDisc=False, useMulti=True. """ writer = CdWriter(device="/dev/cdrw", scsiId="0,0,0", unittest=True) writer._deviceSupportsMulti = False boundaries = writer._getBoundaries(entireDisc=False, useMulti=True) self.assertEqual(None, boundaries) def testCapacity_018(self): """ Test _getBoundaries when self.deviceSupportsMulti is False; entireDisc=True, useMulti=True. """ writer = CdWriter(device="/dev/cdrw", scsiId="0,0,0", unittest=True) writer._deviceSupportsMulti = False boundaries = writer._getBoundaries(entireDisc=True, useMulti=True) self.assertEqual(None, boundaries) def testCapacity_019(self): """ Test _getBoundaries when self.deviceSupportsMulti is False; entireDisc=True, useMulti=False. """ writer = CdWriter(device="/dev/cdrw", scsiId="0,0,0", unittest=True) writer._deviceSupportsMulti = False boundaries = writer._getBoundaries(entireDisc=False, useMulti=False) self.assertEqual(None, boundaries) def testCapacity_020(self): """ Test _getBoundaries when self.deviceSupportsMulti is False; entireDisc=False, useMulti=False. """ writer = CdWriter(device="/dev/cdrw", scsiId="0,0,0", unittest=True) writer._deviceSupportsMulti = False boundaries = writer._getBoundaries(entireDisc=False, useMulti=False) self.assertEqual(None, boundaries) def testCapacity_021(self): """ Test _getBoundaries when self.deviceSupportsMulti is True; entireDisc=True, useMulti=True. """ writer = CdWriter(device="/dev/cdrw", scsiId="0,0,0", unittest=True) writer._deviceSupportsMulti = True boundaries = writer._getBoundaries(entireDisc=True, useMulti=True) self.assertEqual(None, boundaries) def testCapacity_022(self): """ Test _getBoundaries when self.deviceSupportsMulti is True; entireDisc=True, useMulti=False. """ writer = CdWriter(device="/dev/cdrw", scsiId="0,0,0", unittest=True) writer._deviceSupportsMulti = True boundaries = writer._getBoundaries(entireDisc=True, useMulti=False) self.assertEqual(None, boundaries) def testCapacity_023(self): """ Test _calculateCapacity for boundaries of (321342, 330042) and MEDIA_CDRW_74. This was a bug fixed for v2.1.2. """ expectedUsed = 330042 * 2048.0 # 330042 sectors expectedAvailable = 0 # nothing should be available media = MediaDefinition(MEDIA_CDRW_74) boundaries = (321342, 330042) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((321342, 330042), capacity.boundaries) def testCapacity_024(self): """ Test _calculateCapacity for boundaries of (0, 330042) and MEDIA_CDRW_74. This was a bug fixed for v2.1.3. """ expectedUsed = 330042 * 2048.0 # 330042 sectors expectedAvailable = 0 # nothing should be available media = MediaDefinition(MEDIA_CDRW_74) boundaries = (0, 330042) capacity = CdWriter._calculateCapacity(media, boundaries) self.assertEqual(expectedUsed, capacity.bytesUsed) self.assertEqual(expectedAvailable, capacity.bytesAvailable) self.assertEqual((0, 330042), capacity.boundaries) ######################################### # Test methods that build argument lists ######################################### def testBuildArgs_001(self): """ Test _buildOpenTrayArgs(). """ args = CdWriter._buildOpenTrayArgs(device="/dev/stuff") self.assertEqual(["/dev/stuff"], args) def testBuildArgs_002(self): """ Test _buildCloseTrayArgs(). """ args = CdWriter._buildCloseTrayArgs(device="/dev/stuff") self.assertEqual(["-t", "/dev/stuff"], args) def testBuildArgs_003(self): """ Test _buildPropertiesArgs(). """ args = CdWriter._buildPropertiesArgs(hardwareId="0,0,0") self.assertEqual(["-prcap", "dev=0,0,0"], args) def testBuildArgs_004(self): """ Test _buildBoundariesArgs(). """ args = CdWriter._buildBoundariesArgs(hardwareId="ATA:0,0,0") self.assertEqual(["-msinfo", "dev=ATA:0,0,0"], args) def testBuildArgs_005(self): """ Test _buildBoundariesArgs(). """ args = CdWriter._buildBoundariesArgs(hardwareId="ATAPI:0,0,0") self.assertEqual(["-msinfo", "dev=ATAPI:0,0,0"], args) def testBuildArgs_006(self): """ Test _buildBlankArgs(), default drive speed. """ args = CdWriter._buildBlankArgs(hardwareId="ATA:0,0,0") self.assertEqual(["-v", "blank=fast", "dev=ATA:0,0,0"], args) def testBuildArgs_007(self): """ Test _buildBlankArgs(), default drive speed. """ args = CdWriter._buildBlankArgs(hardwareId="ATAPI:0,0,0") self.assertEqual(["-v", "blank=fast", "dev=ATAPI:0,0,0"], args) def testBuildArgs_008(self): """ Test _buildBlankArgs(), with None for drive speed. """ args = CdWriter._buildBlankArgs(hardwareId="0,0,0", driveSpeed=None) self.assertEqual(["-v", "blank=fast", "dev=0,0,0"], args) def testBuildArgs_009(self): """ Test _buildBlankArgs(), with 1 for drive speed. """ args = CdWriter._buildBlankArgs(hardwareId="0,0,0", driveSpeed=1) self.assertEqual(["-v", "blank=fast", "speed=1", "dev=0,0,0"], args) def testBuildArgs_010(self): """ Test _buildBlankArgs(), with 5 for drive speed. """ args = CdWriter._buildBlankArgs(hardwareId="ATA:1,2,3", driveSpeed=5) self.assertEqual(["-v", "blank=fast", "speed=5", "dev=ATA:1,2,3"], args) def testBuildArgs_011(self): """ Test _buildBlankArgs(), with 5 for drive speed. """ args = CdWriter._buildBlankArgs(hardwareId="ATAPI:1,2,3", driveSpeed=5) self.assertEqual(["-v", "blank=fast", "speed=5", "dev=ATAPI:1,2,3"], args) def testBuildArgs_012(self): """ Test _buildWriteArgs(), default drive speed and writeMulti. """ args = CdWriter._buildWriteArgs(hardwareId="0,0,0", imagePath="/whatever") self.assertEqual(["-v", "dev=0,0,0", "-multi", "-data", "/whatever"], args) def testBuildArgs_013(self): """ Test _buildWriteArgs(), None for drive speed, True for writeMulti. """ args = CdWriter._buildWriteArgs(hardwareId="0,0,0", imagePath="/whatever", driveSpeed=None, writeMulti=True) self.assertEqual(["-v", "dev=0,0,0", "-multi", "-data", "/whatever"], args) def testBuildArgs_014(self): """ Test _buildWriteArgs(), None for drive speed, False for writeMulti. """ args = CdWriter._buildWriteArgs(hardwareId="0,0,0", imagePath="/whatever", driveSpeed=None, writeMulti=False) self.assertEqual(["-v", "dev=0,0,0", "-data", "/whatever"], args) def testBuildArgs_015(self): """ Test _buildWriteArgs(), 1 for drive speed, True for writeMulti. """ args = CdWriter._buildWriteArgs(hardwareId="0,0,0", imagePath="/whatever", driveSpeed=1, writeMulti=True) self.assertEqual(["-v", "speed=1", "dev=0,0,0", "-multi", "-data", "/whatever"], args) def testBuildArgs_016(self): """ Test _buildWriteArgs(), 5 for drive speed, True for writeMulti. """ args = CdWriter._buildWriteArgs(hardwareId="0,1,2", imagePath="/whatever", driveSpeed=5, writeMulti=True) self.assertEqual(["-v", "speed=5", "dev=0,1,2", "-multi", "-data", "/whatever"], args) def testBuildArgs_017(self): """ Test _buildWriteArgs(), 1 for drive speed, False for writeMulti. """ args = CdWriter._buildWriteArgs(hardwareId="0,0,0", imagePath="/dvl/stuff/whatever/more", driveSpeed=1, writeMulti=False) self.assertEqual(["-v", "speed=1", "dev=0,0,0", "-data", "/dvl/stuff/whatever/more"], args) def testBuildArgs_018(self): """ Test _buildWriteArgs(), 5 for drive speed, False for writeMulti. """ args = CdWriter._buildWriteArgs(hardwareId="ATA:1,2,3", imagePath="/whatever", driveSpeed=5, writeMulti=False) self.assertEqual(["-v", "speed=5", "dev=ATA:1,2,3", "-data", "/whatever"], args) def testBuildArgs_019(self): """ Test _buildWriteArgs(), 5 for drive speed, False for writeMulti. """ args = CdWriter._buildWriteArgs(hardwareId="ATAPI:1,2,3", imagePath="/whatever", driveSpeed=5, writeMulti=False) self.assertEqual(["-v", "speed=5", "dev=ATAPI:1,2,3", "-data", "/whatever"], args) ########################################## # Test methods that parse cdrecord output ########################################## def testParseOutput_001(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example. """ output = [ "268582,302230\n", ] boundaries = CdWriter._parseBoundariesOutput(output) self.assertEqual((268582, 302230), boundaries) def testParseOutput_002(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, lots of extra whitespace around the values. """ output = [ " 268582 , 302230 \n", ] boundaries = CdWriter._parseBoundariesOutput(output) self.assertEqual((268582, 302230), boundaries) def testParseOutput_003(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, lots of extra garbage after the first line. """ output = [ "268582,302230\n", "more\n", "bogus\n", "crap\n", "here\n", "to\n", "confuse\n", "things\n", ] boundaries = CdWriter._parseBoundariesOutput(output) self.assertEqual((268582, 302230), boundaries) def testParseOutput_004(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, lots of extra garbage before the first line. """ output = [ "more\n", "bogus\n", "crap\n", "here\n", "to\n", "confuse\n", "things\n", "268582,302230\n", ] self.assertRaises(IOError, CdWriter._parseBoundariesOutput, output) def testParseOutput_005(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, with first value converted to negative. """ output = [ "-268582,302230\n", ] self.assertRaises(IOError, CdWriter._parseBoundariesOutput, output) def testParseOutput_006(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, with second value converted to negative. """ output = [ "268582,-302230\n", ] self.assertRaises(IOError, CdWriter._parseBoundariesOutput, output) def testParseOutput_007(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, with first value converted to zero. """ output = [ "0,302230\n", ] boundaries = CdWriter._parseBoundariesOutput(output) self.assertEqual((0, 302230), boundaries) def testParseOutput_008(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, with second value converted to zero. """ output = [ "268582,0\n", ] boundaries = CdWriter._parseBoundariesOutput(output) self.assertEqual((268582, 0), boundaries) def testParseOutput_009(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, with first value converted to negative and second value converted to zero. """ output = [ "-268582,0\n", ] self.assertRaises(IOError, CdWriter._parseBoundariesOutput, output) def testParseOutput_010(self): """ Test _parseBoundariesOutput() for valid data, taken from a real example, with first value converted to zero and second value converted to negative. """ output = [ "0,-302230\n", ] self.assertRaises(IOError, CdWriter._parseBoundariesOutput, output) def testParseOutput_011(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including stderr and stdout mixed together. """ output = [ "scsidev: '0,0,0'\n", "scsibus: 0 target: 0 lun: 0\n", "Linux sg driver version: 3.1.22\n", "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Device type : Removable CD-ROM\n", "Version : 0\n", "Response Format: 1\n", "Vendor_info : 'SONY '\n", "Identifikation : 'CD-RW CRX140E '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read multi-session CDs\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Loading mechanism type: tray\n", " Does support ejection of CD via START/STOP command\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", " Buffer size in KB: 4096\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual("Removable CD-ROM", deviceType) self.assertEqual("SONY", deviceVendor) self.assertEqual("CD-RW CRX140E", deviceId) self.assertEqual(4096.0 * 1024.0, deviceBufferSize) self.assertEqual(True, deviceSupportsMulti) self.assertEqual(True, deviceHasTray) self.assertEqual(True, deviceCanEject) def testParseOutput_012(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including only stdout. """ output = [ "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Device type : Removable CD-ROM\n", "Version : 0\n", "Response Format: 1\n", "Vendor_info : 'SONY '\n", "Identifikation : 'CD-RW CRX140E '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read multi-session CDs\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Loading mechanism type: tray\n", " Does support ejection of CD via START/STOP command\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", " Buffer size in KB: 4096\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual("Removable CD-ROM", deviceType) self.assertEqual("SONY", deviceVendor) self.assertEqual("CD-RW CRX140E", deviceId) self.assertEqual(4096.0 * 1024.0, deviceBufferSize) self.assertEqual(True, deviceSupportsMulti) self.assertEqual(True, deviceHasTray) self.assertEqual(True, deviceCanEject) def testParseOutput_013(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including stderr and stdout mixed together, device type removed. """ output = [ "scsidev: '0,0,0'\n", "scsibus: 0 target: 0 lun: 0\n", "Linux sg driver version: 3.1.22\n", "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Version : 0\n", "Response Format: 1\n", "Vendor_info : 'SONY '\n", "Identifikation : 'CD-RW CRX140E '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read multi-session CDs\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Loading mechanism type: tray\n", " Does support ejection of CD via START/STOP command\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", " Buffer size in KB: 4096\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual(None, deviceType) self.assertEqual("SONY", deviceVendor) self.assertEqual("CD-RW CRX140E", deviceId) self.assertEqual(4096.0 * 1024.0, deviceBufferSize) self.assertEqual(True, deviceSupportsMulti) self.assertEqual(True, deviceHasTray) self.assertEqual(True, deviceCanEject) def testParseOutput_014(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including stderr and stdout mixed together, device vendor removed. """ output = [ "scsidev: '0,0,0'\n", "scsibus: 0 target: 0 lun: 0\n", "Linux sg driver version: 3.1.22\n", "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Device type : Removable CD-ROM\n", "Version : 0\n", "Response Format: 1\n", "Identifikation : 'CD-RW CRX140E '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read multi-session CDs\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Loading mechanism type: tray\n", " Does support ejection of CD via START/STOP command\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", " Buffer size in KB: 4096\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual("Removable CD-ROM", deviceType) self.assertEqual(None, deviceVendor) self.assertEqual("CD-RW CRX140E", deviceId) self.assertEqual(4096.0 * 1024.0, deviceBufferSize) self.assertEqual(True, deviceSupportsMulti) self.assertEqual(True, deviceHasTray) self.assertEqual(True, deviceCanEject) def testParseOutput_015(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including stderr and stdout mixed together, device id removed. """ output = [ "scsidev: '0,0,0'\n", "scsibus: 0 target: 0 lun: 0\n", "Linux sg driver version: 3.1.22\n", "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Device type : Removable CD-ROM\n", "Version : 0\n", "Response Format: 1\n", "Vendor_info : 'SONY '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read multi-session CDs\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Loading mechanism type: tray\n", " Does support ejection of CD via START/STOP command\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", " Buffer size in KB: 4096\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual("Removable CD-ROM", deviceType) self.assertEqual("SONY", deviceVendor) self.assertEqual(None, deviceId) self.assertEqual(4096.0 * 1024.0, deviceBufferSize) self.assertEqual(True, deviceSupportsMulti) self.assertEqual(True, deviceHasTray) self.assertEqual(True, deviceCanEject) def testParseOutput_016(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including stderr and stdout mixed together, buffer size removed. """ output = [ "scsidev: '0,0,0'\n", "scsibus: 0 target: 0 lun: 0\n", "Linux sg driver version: 3.1.22\n", "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Device type : Removable CD-ROM\n", "Version : 0\n", "Response Format: 1\n", "Vendor_info : 'SONY '\n", "Identifikation : 'CD-RW CRX140E '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read multi-session CDs\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Loading mechanism type: tray\n", " Does support ejection of CD via START/STOP command\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual("Removable CD-ROM", deviceType) self.assertEqual("SONY", deviceVendor) self.assertEqual("CD-RW CRX140E", deviceId) self.assertEqual(None, deviceBufferSize) self.assertEqual(True, deviceSupportsMulti) self.assertEqual(True, deviceHasTray) self.assertEqual(True, deviceCanEject) def testParseOutput_017(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including stderr and stdout mixed together, "supports multi" removed. """ output = [ "scsidev: '0,0,0'\n", "scsibus: 0 target: 0 lun: 0\n", "Linux sg driver version: 3.1.22\n", "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Device type : Removable CD-ROM\n", "Version : 0\n", "Response Format: 1\n", "Vendor_info : 'SONY '\n", "Identifikation : 'CD-RW CRX140E '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Loading mechanism type: tray\n", " Does support ejection of CD via START/STOP command\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", " Buffer size in KB: 4096\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual("Removable CD-ROM", deviceType) self.assertEqual("SONY", deviceVendor) self.assertEqual("CD-RW CRX140E", deviceId) self.assertEqual(4096.0 * 1024.0, deviceBufferSize) self.assertEqual(False, deviceSupportsMulti) self.assertEqual(True, deviceHasTray) self.assertEqual(True, deviceCanEject) def testParseOutput_018(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including stderr and stdout mixed together, "has tray" removed. """ output = [ "scsidev: '0,0,0'\n", "scsibus: 0 target: 0 lun: 0\n", "Linux sg driver version: 3.1.22\n", "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Device type : Removable CD-ROM\n", "Version : 0\n", "Response Format: 1\n", "Vendor_info : 'SONY '\n", "Identifikation : 'CD-RW CRX140E '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read multi-session CDs\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Does support ejection of CD via START/STOP command\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", " Buffer size in KB: 4096\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual("Removable CD-ROM", deviceType) self.assertEqual("SONY", deviceVendor) self.assertEqual("CD-RW CRX140E", deviceId) self.assertEqual(4096.0 * 1024.0, deviceBufferSize) self.assertEqual(True, deviceSupportsMulti) self.assertEqual(False, deviceHasTray) self.assertEqual(True, deviceCanEject) def testParseOutput_019(self): """ Test _parsePropertiesOutput() for valid data, taken from a real example, including stderr and stdout mixed together, "can eject" removed. """ output = [ "scsidev: '0,0,0'\n", "scsibus: 0 target: 0 lun: 0\n", "Linux sg driver version: 3.1.22\n", "Cdrecord 1.10 (i686-pc-linux-gnu) Copyright (C) 1995-2001 J\xf6rg Schilling\n", "Using libscg version 'schily-0.5'\n", "Device type : Removable CD-ROM\n", "Version : 0\n", "Response Format: 1\n", "Vendor_info : 'SONY '\n", "Identifikation : 'CD-RW CRX140E '\n", "Revision : '1.0n'\n", "Device seems to be: Generic mmc CD-RW.\n", "\n", "Drive capabilities, per page 2A:\n", "\n", " Does read CD-R media\n", " Does write CD-R media\n", " Does read CD-RW media\n", " Does write CD-RW media\n", " Does not read DVD-ROM media\n", " Does not read DVD-R media\n", " Does not write DVD-R media\n", " Does not read DVD-RAM media\n", " Does not write DVD-RAM media\n", " Does support test writing\n", "\n", " Does read Mode 2 Form 1 blocks\n", " Does read Mode 2 Form 2 blocks\n", " Does read digital audio blocks\n", " Does restart non-streamed digital audio reads accurately\n", " Does not support BURN-Proof (Sanyo)\n", " Does read multi-session CDs\n", " Does read fixed-packet CD media using Method 2\n", " Does not read CD bar code\n", " Does not read R-W subcode information\n", " Does read raw P-W subcode data from lead in\n", " Does return CD media catalog number\n", " Does return CD ISRC information\n", " Does not support C2 error pointers\n", " Does not deliver composite A/V data\n", "\n", " Does play audio CDs\n", " Number of volume control levels: 256\n", " Does support individual volume control setting for each channel\n", " Does support independent mute setting for each channel\n", " Does not support digital output on port 1\n", " Does not support digital output on port 2\n", "\n", " Loading mechanism type: tray\n", " Does not lock media on power up via prevent jumper\n", " Does allow media to be locked in the drive via PREVENT/ALLOW command\n", " Is not currently in a media-locked state\n", " Does not support changing side of disk\n", " Does not have load-empty-slot-in-changer feature\n", " Does not support Individual Disk Present feature\n", "\n", " Maximum read speed in kB/s: 5645\n", " Current read speed in kB/s: 3528\n", " Maximum write speed in kB/s: 1411\n", " Current write speed in kB/s: 706\n", " Buffer size in KB: 4096\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual("Removable CD-ROM", deviceType) self.assertEqual("SONY", deviceVendor) self.assertEqual("CD-RW CRX140E", deviceId) self.assertEqual(4096.0 * 1024.0, deviceBufferSize) self.assertEqual(True, deviceSupportsMulti) self.assertEqual(True, deviceHasTray) self.assertEqual(False, deviceCanEject) def testParseOutput_020(self): """ Test _parsePropertiesOutput() for nonsensical data, just a bunch of empty lines. """ output = [ "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", "\n", ] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual(None, deviceType) self.assertEqual(None, deviceVendor) self.assertEqual(None, deviceId) self.assertEqual(None, deviceBufferSize) self.assertEqual(False, deviceSupportsMulti) self.assertEqual(False, deviceHasTray) self.assertEqual(False, deviceCanEject) def testParseOutput_021(self): """ Test _parsePropertiesOutput() for nonsensical data, just an empty list. """ output = [] ( deviceType, deviceVendor, deviceId, deviceBufferSize, deviceSupportsMulti, deviceHasTray, deviceCanEject, ) = CdWriter._parsePropertiesOutput(output) self.assertEqual(None, deviceType) self.assertEqual(None, deviceVendor) self.assertEqual(None, deviceId) self.assertEqual(None, deviceBufferSize) self.assertEqual(False, deviceSupportsMulti) self.assertEqual(False, deviceHasTray) self.assertEqual(False, deviceCanEject) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1569562 cedar_backup3-3.8.1/tests/test_cli.py0000644000000000000000000244537514567004737014522 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2005,2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests command-line interface functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/cli.py. Code Coverage ============= This module contains individual tests for the many of the public functions and classes implemented in cli.py. Where possible, we test functions that print output by passing a custom file descriptor. Sometimes, we only ensure that a function or method runs without failure, and we don't validate what its result is or what it prints out. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a CLITESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from getopt import GetoptError from os.path import exists, isabs, isdir, isfile, islink from CedarBackup3.action import executeCollect, executePurge, executeRebuild, executeStage, executeStore, executeValidate from CedarBackup3.cli import Options, _ActionSet, _diagnostics, _usage, _version from CedarBackup3.config import ( ActionDependencies, ExtendedAction, ExtensionsConfig, LocalPeer, OptionsConfig, PeersConfig, PostActionHook, PreActionHook, RemotePeer, ) from CedarBackup3.testutil import captureOutput, configureLogging, failUnlessAssignRaises ####################################################################### # Test Case Classes ####################################################################### ###################### # TestFunctions class ###################### class TestFunctions(unittest.TestCase): """Tests for the public functions.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ######################## # Test simple functions ######################## def testSimpleFuncs_001(self): """ Test that the _usage() function runs without errors. We don't care what the output is, and we don't check. """ captureOutput(_usage) def testSimpleFuncs_002(self): """ Test that the _version() function runs without errors. We don't care what the output is, and we don't check. """ captureOutput(_version) def testSimpleFuncs_003(self): """ Test that the _diagnostics() function runs without errors. We don't care what the output is, and we don't check. """ captureOutput(_diagnostics) #################### # TestOptions class #################### class TestOptions(unittest.TestCase): """Tests for the Options class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = Options() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no arguments. """ options = Options() self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_002(self): """ Test constructor with validate=False, no other arguments. """ options = Options(validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_003(self): """ Test constructor with argumentList=[], validate=False. """ options = Options(argumentList=[], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_004(self): """ Test constructor with argumentString="", validate=False. """ options = Options(argumentString="", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_005(self): """ Test constructor with argumentList=["--help", ], validate=False. """ options = Options(argumentList=["--help"], validate=False) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_006(self): """ Test constructor with argumentString="--help", validate=False. """ options = Options(argumentString="--help", validate=False) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_007(self): """ Test constructor with argumentList=["-h", ], validate=False. """ options = Options(argumentList=["-h"], validate=False) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_008(self): """ Test constructor with argumentString="-h", validate=False. """ options = Options(argumentString="-h", validate=False) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_009(self): """ Test constructor with argumentList=["--version", ], validate=False. """ options = Options(argumentList=["--version"], validate=False) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_010(self): """ Test constructor with argumentString="--version", validate=False. """ options = Options(argumentString="--version", validate=False) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_011(self): """ Test constructor with argumentList=["-V", ], validate=False. """ options = Options(argumentList=["-V"], validate=False) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_012(self): """ Test constructor with argumentString="-V", validate=False. """ options = Options(argumentString="-V", validate=False) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_013(self): """ Test constructor with argumentList=["--verbose", ], validate=False. """ options = Options(argumentList=["--verbose"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_014(self): """ Test constructor with argumentString="--verbose", validate=False. """ options = Options(argumentString="--verbose", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_015(self): """ Test constructor with argumentList=["-b", ], validate=False. """ options = Options(argumentList=["-b"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_016(self): """ Test constructor with argumentString="-b", validate=False. """ options = Options(argumentString="-b", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_017(self): """ Test constructor with argumentList=["--quiet", ], validate=False. """ options = Options(argumentList=["--quiet"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(True, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_018(self): """ Test constructor with argumentString="--quiet", validate=False. """ options = Options(argumentString="--quiet", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(True, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_019(self): """ Test constructor with argumentList=["-q", ], validate=False. """ options = Options(argumentList=["-q"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(True, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_020(self): """ Test constructor with argumentString="-q", validate=False. """ options = Options(argumentString="-q", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(True, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_021(self): """ Test constructor with argumentList=["--config", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["--config"], validate=False) def testConstructor_022(self): """ Test constructor with argumentString="--config", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="--config", validate=False) def testConstructor_023(self): """ Test constructor with argumentList=["-c", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["-c"], validate=False) def testConstructor_024(self): """ Test constructor with argumentString="-c", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="-c", validate=False) def testConstructor_025(self): """ Test constructor with argumentList=["--config", "something", ], validate=False. """ options = Options(argumentList=["--config", "something"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual("something", options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_026(self): """ Test constructor with argumentString="--config something", validate=False. """ options = Options(argumentString="--config something", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual("something", options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_027(self): """ Test constructor with argumentList=["-c", "something", ], validate=False. """ options = Options(argumentList=["-c", "something"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual("something", options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_028(self): """ Test constructor with argumentString="-c something", validate=False. """ options = Options(argumentString="-c something", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual("something", options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_029(self): """ Test constructor with argumentList=["--full", ], validate=False. """ options = Options(argumentList=["--full"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(True, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_030(self): """ Test constructor with argumentString="--full", validate=False. """ options = Options(argumentString="--full", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(True, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_031(self): """ Test constructor with argumentList=["-f", ], validate=False. """ options = Options(argumentList=["-f"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(True, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_032(self): """ Test constructor with argumentString="-f", validate=False. """ options = Options(argumentString="-f", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(True, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_033(self): """ Test constructor with argumentList=["--logfile", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["--logfile"], validate=False) def testConstructor_034(self): """ Test constructor with argumentString="--logfile", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="--logfile", validate=False) def testConstructor_035(self): """ Test constructor with argumentList=["-l", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["-l"], validate=False) def testConstructor_036(self): """ Test constructor with argumentString="-l", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="-l", validate=False) def testConstructor_037(self): """ Test constructor with argumentList=["--logfile", "something", ], validate=False. """ options = Options(argumentList=["--logfile", "something"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual("something", options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_038(self): """ Test constructor with argumentString="--logfile something", validate=False. """ options = Options(argumentString="--logfile something", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual("something", options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_039(self): """ Test constructor with argumentList=["-l", "something", ], validate=False. """ options = Options(argumentList=["-l", "something"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual("something", options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_040(self): """ Test constructor with argumentString="-l something", validate=False. """ options = Options(argumentString="-l something", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual("something", options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_041(self): """ Test constructor with argumentList=["--owner", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["--owner"], validate=False) def testConstructor_042(self): """ Test constructor with argumentString="--owner", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="--owner", validate=False) def testConstructor_043(self): """ Test constructor with argumentList=["-o", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["-o"], validate=False) def testConstructor_044(self): """ Test constructor with argumentString="-o", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="-o", validate=False) def testConstructor_045(self): """ Test constructor with argumentList=["--owner", "something", ], validate=False. """ self.assertRaises(ValueError, Options, argumentList=["--owner", "something"], validate=False) def testConstructor_046(self): """ Test constructor with argumentString="--owner something", validate=False. """ self.assertRaises(ValueError, Options, argumentString="--owner something", validate=False) def testConstructor_047(self): """ Test constructor with argumentList=["-o", "something", ], validate=False. """ self.assertRaises(ValueError, Options, argumentList=["-o", "something"], validate=False) def testConstructor_048(self): """ Test constructor with argumentString="-o something", validate=False. """ self.assertRaises(ValueError, Options, argumentString="-o something", validate=False) def testConstructor_049(self): """ Test constructor with argumentList=["--owner", "a:b", ], validate=False. """ options = Options(argumentList=["--owner", "a:b"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(("a", "b"), options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_050(self): """ Test constructor with argumentString="--owner a:b", validate=False. """ options = Options(argumentString="--owner a:b", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(("a", "b"), options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_051(self): """ Test constructor with argumentList=["-o", "a:b", ], validate=False. """ options = Options(argumentList=["-o", "a:b"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(("a", "b"), options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_052(self): """ Test constructor with argumentString="-o a:b", validate=False. """ options = Options(argumentString="-o a:b", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(("a", "b"), options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_053(self): """ Test constructor with argumentList=["--mode", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["--mode"], validate=False) def testConstructor_054(self): """ Test constructor with argumentString="--mode", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="--mode", validate=False) def testConstructor_055(self): """ Test constructor with argumentList=["-m", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["-m"], validate=False) def testConstructor_056(self): """ Test constructor with argumentString="-m", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="-m", validate=False) def testConstructor_057(self): """ Test constructor with argumentList=["--mode", "something", ], validate=False. """ self.assertRaises(ValueError, Options, argumentList=["--mode", "something"], validate=False) def testConstructor_058(self): """ Test constructor with argumentString="--mode something", validate=False. """ self.assertRaises(ValueError, Options, argumentString="--mode something", validate=False) def testConstructor_059(self): """ Test constructor with argumentList=["-m", "something", ], validate=False. """ self.assertRaises(ValueError, Options, argumentList=["-m", "something"], validate=False) def testConstructor_060(self): """ Test constructor with argumentString="-m something", validate=False. """ self.assertRaises(ValueError, Options, argumentString="-m something", validate=False) def testConstructor_061(self): """ Test constructor with argumentList=["--mode", "631", ], validate=False. """ options = Options(argumentList=["--mode", "631"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o631, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_062(self): """ Test constructor with argumentString="--mode 631", validate=False. """ options = Options(argumentString="--mode 631", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o631, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_063(self): """ Test constructor with argumentList=["-m", "631", ], validate=False. """ options = Options(argumentList=["-m", "631"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o631, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_064(self): """ Test constructor with argumentString="-m 631", validate=False. """ options = Options(argumentString="-m 631", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o631, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_065(self): """ Test constructor with argumentList=["--output", ], validate=False. """ options = Options(argumentList=["--output"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(True, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_066(self): """ Test constructor with argumentString="--output", validate=False. """ options = Options(argumentString="--output", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(True, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_067(self): """ Test constructor with argumentList=["-O", ], validate=False. """ options = Options(argumentList=["-O"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(True, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_068(self): """ Test constructor with argumentString="-O", validate=False. """ options = Options(argumentString="-O", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(True, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_069(self): """ Test constructor with argumentList=["--debug", ], validate=False. """ options = Options(argumentList=["--debug"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_070(self): """ Test constructor with argumentString="--debug", validate=False. """ options = Options(argumentString="--debug", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_071(self): """ Test constructor with argumentList=["-d", ], validate=False. """ options = Options(argumentList=["-d"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_072(self): """ Test constructor with argumentString="-d", validate=False. """ options = Options(argumentString="-d", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_073(self): """ Test constructor with argumentList=["--stack", ], validate=False. """ options = Options(argumentList=["--stack"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(True, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_074(self): """ Test constructor with argumentString="--stack", validate=False. """ options = Options(argumentString="--stack", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(True, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_075(self): """ Test constructor with argumentList=["-s", ], validate=False. """ options = Options(argumentList=["-s"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(True, options.stacktrace) self.assertEqual([], options.actions) def testConstructor_076(self): """ Test constructor with argumentString="-s", validate=False. """ options = Options(argumentString="-s", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(True, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_077(self): """ Test constructor with argumentList=["all", ], validate=False. """ options = Options(argumentList=["all"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["all"], options.actions) def testConstructor_078(self): """ Test constructor with argumentString="all", validate=False. """ options = Options(argumentString="all", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["all"], options.actions) def testConstructor_079(self): """ Test constructor with argumentList=["collect", ], validate=False. """ options = Options(argumentList=["collect"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect"], options.actions) def testConstructor_080(self): """ Test constructor with argumentString="collect", validate=False. """ options = Options(argumentString="collect", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect"], options.actions) def testConstructor_081(self): """ Test constructor with argumentList=["stage", ], validate=False. """ options = Options(argumentList=["stage"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["stage"], options.actions) def testConstructor_082(self): """ Test constructor with argumentString="stage", validate=False. """ options = Options(argumentString="stage", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["stage"], options.actions) def testConstructor_083(self): """ Test constructor with argumentList=["store", ], validate=False. """ options = Options(argumentList=["store"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["store"], options.actions) def testConstructor_084(self): """ Test constructor with argumentString="store", validate=False. """ options = Options(argumentString="store", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["store"], options.actions) def testConstructor_085(self): """ Test constructor with argumentList=["purge", ], validate=False. """ options = Options(argumentList=["purge"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["purge"], options.actions) def testConstructor_086(self): """ Test constructor with argumentString="purge", validate=False. """ options = Options(argumentString="purge", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["purge"], options.actions) def testConstructor_087(self): """ Test constructor with argumentList=["rebuild", ], validate=False. """ options = Options(argumentList=["rebuild"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["rebuild"], options.actions) def testConstructor_088(self): """ Test constructor with argumentString="rebuild", validate=False. """ options = Options(argumentString="rebuild", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["rebuild"], options.actions) def testConstructor_089(self): """ Test constructor with argumentList=["validate", ], validate=False. """ options = Options(argumentList=["validate"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["validate"], options.actions) def testConstructor_090(self): """ Test constructor with argumentString="validate", validate=False. """ options = Options(argumentString="validate", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["validate"], options.actions) def testConstructor_091(self): """ Test constructor with argumentList=["collect", "all", ], validate=False. """ options = Options(argumentList=["collect", "all"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "all"], options.actions) def testConstructor_092(self): """ Test constructor with argumentString="collect all", validate=False. """ options = Options(argumentString="collect all", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "all"], options.actions) def testConstructor_093(self): """ Test constructor with argumentList=["collect", "rebuild", ], validate=False. """ options = Options(argumentList=["collect", "rebuild"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "rebuild"], options.actions) def testConstructor_094(self): """ Test constructor with argumentString="collect rebuild", validate=False. """ options = Options(argumentString="collect rebuild", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "rebuild"], options.actions) def testConstructor_095(self): """ Test constructor with argumentList=["collect", "validate", ], validate=False. """ options = Options(argumentList=["collect", "validate"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "validate"], options.actions) def testConstructor_096(self): """ Test constructor with argumentString="collect validate", validate=False. """ options = Options(argumentString="collect validate", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "validate"], options.actions) def testConstructor_097(self): """ Test constructor with argumentList=["-d", "--verbose", "-O", "--mode", "600", "collect", "stage", ], validate=False. """ options = Options(argumentList=["-d", "--verbose", "-O", "--mode", "600", "collect", "stage"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o600, options.mode) self.assertEqual(True, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "stage"], options.actions) def testConstructor_098(self): """ Test constructor with argumentString="-d --verbose -O --mode 600 collect stage", validate=False. """ options = Options(argumentString="-d --verbose -O --mode 600 collect stage", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o600, options.mode) self.assertEqual(True, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "stage"], options.actions) def testConstructor_099(self): """ Test constructor with argumentList=[], validate=True. """ self.assertRaises(ValueError, Options, argumentList=[], validate=True) def testConstructor_100(self): """ Test constructor with argumentString="", validate=True. """ self.assertRaises(ValueError, Options, argumentString="", validate=True) def testConstructor_101(self): """ Test constructor with argumentList=["--help", ], validate=True. """ options = Options(argumentList=["--help"], validate=True) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_102(self): """ Test constructor with argumentString="--help", validate=True. """ options = Options(argumentString="--help", validate=True) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_103(self): """ Test constructor with argumentList=["-h", ], validate=True. """ options = Options(argumentList=["-h"], validate=True) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_104(self): """ Test constructor with argumentString="-h", validate=True. """ options = Options(argumentString="-h", validate=True) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_105(self): """ Test constructor with argumentList=["--version", ], validate=True. """ options = Options(argumentList=["--version"], validate=True) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_106(self): """ Test constructor with argumentString="--version", validate=True. """ options = Options(argumentString="--version", validate=True) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_107(self): """ Test constructor with argumentList=["-V", ], validate=True. """ options = Options(argumentList=["-V"], validate=True) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_108(self): """ Test constructor with argumentString="-V", validate=True. """ options = Options(argumentString="-V", validate=True) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_109(self): """ Test constructor with argumentList=["--verbose", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--verbose"], validate=True) def testConstructor_110(self): """ Test constructor with argumentString="--verbose", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--verbose", validate=True) def testConstructor_111(self): """ Test constructor with argumentList=["-b", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-b"], validate=True) def testConstructor_112(self): """ Test constructor with argumentString="-b", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-b", validate=True) def testConstructor_113(self): """ Test constructor with argumentList=["--quiet", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--quiet"], validate=True) def testConstructor_114(self): """ Test constructor with argumentString="--quiet", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--quiet", validate=True) def testConstructor_115(self): """ Test constructor with argumentList=["-q", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-q"], validate=True) def testConstructor_116(self): """ Test constructor with argumentString="-q", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-q", validate=True) def testConstructor_117(self): """ Test constructor with argumentList=["--config", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["--config"], validate=True) def testConstructor_118(self): """ Test constructor with argumentString="--config", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="--config", validate=True) def testConstructor_119(self): """ Test constructor with argumentList=["-c", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["-c"], validate=True) def testConstructor_120(self): """ Test constructor with argumentString="-c", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="-c", validate=True) def testConstructor_121(self): """ Test constructor with argumentList=["--config", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--config", "something"], validate=True) def testConstructor_122(self): """ Test constructor with argumentString="--config something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--config something", validate=True) def testConstructor_123(self): """ Test constructor with argumentList=["-c", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-c", "something"], validate=True) def testConstructor_124(self): """ Test constructor with argumentString="-c something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-c something", validate=True) def testConstructor_125(self): """ Test constructor with argumentList=["--full", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--full"], validate=True) def testConstructor_126(self): """ Test constructor with argumentString="--full", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--full", validate=True) def testConstructor_127(self): """ Test constructor with argumentList=["-f", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-f"], validate=True) def testConstructor_128(self): """ Test constructor with argumentString="-f", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-f", validate=True) def testConstructor_129(self): """ Test constructor with argumentList=["--logfile", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["--logfile"], validate=True) def testConstructor_130(self): """ Test constructor with argumentString="--logfile", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="--logfile", validate=True) def testConstructor_131(self): """ Test constructor with argumentList=["-l", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["-l"], validate=True) def testConstructor_132(self): """ Test constructor with argumentString="-l", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="-l", validate=True) def testConstructor_133(self): """ Test constructor with argumentList=["--logfile", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--logfile", "something"], validate=True) def testConstructor_134(self): """ Test constructor with argumentString="--logfile something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--logfile something", validate=True) def testConstructor_135(self): """ Test constructor with argumentList=["-l", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-l", "something"], validate=True) def testConstructor_136(self): """ Test constructor with argumentString="-l something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-l something", validate=True) def testConstructor_137(self): """ Test constructor with argumentList=["--owner", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["--owner"], validate=True) def testConstructor_138(self): """ Test constructor with argumentString="--owner", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="--owner", validate=True) def testConstructor_139(self): """ Test constructor with argumentList=["-o", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["-o"], validate=True) def testConstructor_140(self): """ Test constructor with argumentString="-o", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="-o", validate=True) def testConstructor_141(self): """ Test constructor with argumentList=["--owner", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--owner", "something"], validate=True) def testConstructor_142(self): """ Test constructor with argumentString="--owner something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--owner something", validate=True) def testConstructor_143(self): """ Test constructor with argumentList=["-o", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-o", "something"], validate=True) def testConstructor_144(self): """ Test constructor with argumentString="-o something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-o something", validate=True) def testConstructor_145(self): """ Test constructor with argumentList=["--owner", "a:b", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--owner", "a:b"], validate=True) def testConstructor_146(self): """ Test constructor with argumentString="--owner a:b", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--owner a:b", validate=True) def testConstructor_147(self): """ Test constructor with argumentList=["-o", "a:b", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-o", "a:b"], validate=True) def testConstructor_148(self): """ Test constructor with argumentString="-o a:b", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-o a:b", validate=True) def testConstructor_149(self): """ Test constructor with argumentList=["--mode", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["--mode"], validate=True) def testConstructor_150(self): """ Test constructor with argumentString="--mode", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="--mode", validate=True) def testConstructor_151(self): """ Test constructor with argumentList=["-m", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["-m"], validate=True) def testConstructor_152(self): """ Test constructor with argumentString="-m", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="-m", validate=True) def testConstructor_153(self): """ Test constructor with argumentList=["--mode", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--mode", "something"], validate=True) def testConstructor_154(self): """ Test constructor with argumentString="--mode something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--mode something", validate=True) def testConstructor_155(self): """ Test constructor with argumentList=["-m", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-m", "something"], validate=True) def testConstructor_156(self): """ Test constructor with argumentString="-m something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-m something", validate=True) def testConstructor_157(self): """ Test constructor with argumentList=["--mode", "631", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--mode", "631"], validate=True) def testConstructor_158(self): """ Test constructor with argumentString="--mode 631", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--mode 631", validate=True) def testConstructor_159(self): """ Test constructor with argumentList=["-m", "631", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-m", "631"], validate=True) def testConstructor_160(self): """ Test constructor with argumentString="-m 631", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-m 631", validate=True) def testConstructor_161(self): """ Test constructor with argumentList=["--output", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--output"], validate=True) def testConstructor_162(self): """ Test constructor with argumentString="--output", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--output", validate=True) def testConstructor_163(self): """ Test constructor with argumentList=["-O", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-O"], validate=True) def testConstructor_164(self): """ Test constructor with argumentString="-O", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-O", validate=True) def testConstructor_165(self): """ Test constructor with argumentList=["--debug", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--debug"], validate=True) def testConstructor_166(self): """ Test constructor with argumentString="--debug", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--debug", validate=True) def testConstructor_167(self): """ Test constructor with argumentList=["-d", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-d"], validate=True) def testConstructor_168(self): """ Test constructor with argumentString="-d", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-d", validate=True) def testConstructor_169(self): """ Test constructor with argumentList=["--stack", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--stack"], validate=True) def testConstructor_170(self): """ Test constructor with argumentString="--stack", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--stack", validate=True) def testConstructor_171(self): """ Test constructor with argumentList=["-s", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-s"], validate=True) def testConstructor_172(self): """ Test constructor with argumentString="-s", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-s", validate=True) def testConstructor_173(self): """ Test constructor with argumentList=["all", ], validate=True. """ options = Options(argumentList=["all"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["all"], options.actions) def testConstructor_174(self): """ Test constructor with argumentString="all", validate=True. """ options = Options(argumentString="all", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["all"], options.actions) def testConstructor_175(self): """ Test constructor with argumentList=["collect", ], validate=True. """ options = Options(argumentList=["collect"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect"], options.actions) def testConstructor_176(self): """ Test constructor with argumentString="collect", validate=True. """ options = Options(argumentString="collect", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect"], options.actions) def testConstructor_177(self): """ Test constructor with argumentList=["stage", ], validate=True. """ options = Options(argumentList=["stage"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["stage"], options.actions) def testConstructor_178(self): """ Test constructor with argumentString="stage", validate=True. """ options = Options(argumentString="stage", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["stage"], options.actions) def testConstructor_179(self): """ Test constructor with argumentList=["store", ], validate=True. """ options = Options(argumentList=["store"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["store"], options.actions) def testConstructor_180(self): """ Test constructor with argumentString="store", validate=True. """ options = Options(argumentString="store", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["store"], options.actions) def testConstructor_181(self): """ Test constructor with argumentList=["purge", ], validate=True. """ options = Options(argumentList=["purge"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["purge"], options.actions) def testConstructor_182(self): """ Test constructor with argumentString="purge", validate=True. """ options = Options(argumentString="purge", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["purge"], options.actions) def testConstructor_183(self): """ Test constructor with argumentList=["rebuild", ], validate=True. """ options = Options(argumentList=["rebuild"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["rebuild"], options.actions) def testConstructor_184(self): """ Test constructor with argumentString="rebuild", validate=True. """ options = Options(argumentString="rebuild", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["rebuild"], options.actions) def testConstructor_185(self): """ Test constructor with argumentList=["validate", ], validate=True. """ options = Options(argumentList=["validate"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["validate"], options.actions) def testConstructor_186(self): """ Test constructor with argumentString="validate", validate=True. """ options = Options(argumentString="validate", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["validate"], options.actions) def testConstructor_187(self): """ Test constructor with argumentList=["-d", "--verbose", "-O", "--mode", "600", "collect", "stage", ], validate=True. """ options = Options(argumentList=["-d", "--verbose", "-O", "--mode", "600", "collect", "stage"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o600, options.mode) self.assertEqual(True, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "stage"], options.actions) def testConstructor_188(self): """ Test constructor with argumentString="-d --verbose -O --mode 600 collect stage", validate=True. """ options = Options(argumentString="-d --verbose -O --mode 600 collect stage", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o600, options.mode) self.assertEqual(True, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(["collect", "stage"], options.actions) def testConstructor_189(self): """ Test constructor with argumentList=["--managed", ], validate=False. """ options = Options(argumentList=["--managed"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(True, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_190(self): """ Test constructor with argumentString="--managed", validate=False. """ options = Options(argumentString="--managed", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(True, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_191(self): """ Test constructor with argumentList=["-M", ], validate=False. """ options = Options(argumentList=["-M"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(True, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_192(self): """ Test constructor with argumentString="-M", validate=False. """ options = Options(argumentString="-M", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(True, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_193(self): """ Test constructor with argumentList=["--managed-only", ], validate=False. """ options = Options(argumentList=["--managed-only"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(True, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_194(self): """ Test constructor with argumentString="--managed-only", validate=False. """ options = Options(argumentString="--managed-only", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(True, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_195(self): """ Test constructor with argumentList=["-N", ], validate=False. """ options = Options(argumentList=["-N"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(True, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_196(self): """ Test constructor with argumentString="-N", validate=False. """ options = Options(argumentString="-N", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(True, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_197(self): """ Test constructor with argumentList=["--managed", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--managed"], validate=True) def testConstructor_198(self): """ Test constructor with argumentString="--managed", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--managed", validate=True) def testConstructor_199(self): """ Test constructor with argumentList=["-M", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-M"], validate=True) def testConstructor_200(self): """ Test constructor with argumentString="-M", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-M", validate=True) def testConstructor_201(self): """ Test constructor with argumentList=["--managed-only", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--managed-only"], validate=True) def testConstructor_202(self): """ Test constructor with argumentString="--managed-only", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--managed-only", validate=True) def testConstructor_203(self): """ Test constructor with argumentList=["-N", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-N"], validate=True) def testConstructor_204(self): """ Test constructor with argumentString="-N", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-N", validate=True) def testConstructor_205(self): """ Test constructor with argumentList=["--diagnostics", ], validate=False. """ options = Options(argumentList=["--diagnostics"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_206(self): """ Test constructor with argumentString="--diagnostics", validate=False. """ options = Options(argumentString="--diagnostics", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_207(self): """ Test constructor with argumentList=["-D", ], validate=False. """ options = Options(argumentList=["-D"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_208(self): """ Test constructor with argumentString="-D", validate=False. """ options = Options(argumentString="-D", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_209(self): """ Test constructor with argumentList=["--diagnostics", ], validate=True. """ options = Options(argumentList=["--diagnostics"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_210(self): """ Test constructor with argumentString="--diagnostics", validate=True. """ options = Options(argumentString="--diagnostics", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_211(self): """ Test constructor with argumentList=["-D", ], validate=True. """ options = Options(argumentList=["-D"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual([], options.actions) def testConstructor_212(self): """ Test constructor with argumentString="-D", validate=True. """ options = Options(argumentString="-D", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.config) self.assertEqual(False, options.full) self.assertEqual(False, options.managed) self.assertEqual(False, options.managedOnly) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual([], options.actions) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes at defaults. """ options1 = Options() options2 = Options() self.assertEqual(options1, options2) self.assertTrue(options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(not options1 != options2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes filled in and same. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertEqual(options1, options2) self.assertTrue(options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(not options1 != options2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes filled in, help different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = False options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_004(self): """ Test comparison of two identical objects, all attributes filled in, version different. """ options1 = Options() options2 = Options() options1.help = True options1.version = False options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_005(self): """ Test comparison of two identical objects, all attributes filled in, verbose different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = False options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_006(self): """ Test comparison of two identical objects, all attributes filled in, quiet different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = False options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_007(self): """ Test comparison of two identical objects, all attributes filled in, config different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "whatever" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_008(self): """ Test comparison of two identical objects, all attributes filled in, full different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = False options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_009(self): """ Test comparison of two identical objects, all attributes filled in, logfile different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "stuff" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_010(self): """ Test comparison of two identical objects, all attributes filled in, owner different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("c", "d") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_011(self): """ Test comparison of two identical objects, all attributes filled in, mode different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = 0o600 options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_012(self): """ Test comparison of two identical objects, all attributes filled in, output different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = False options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_013(self): """ Test comparison of two identical objects, all attributes filled in, debug different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = False options1.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_014(self): """ Test comparison of two identical objects, all attributes filled in, stacktrace different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_015(self): """ Test comparison of two identical objects, all attributes filled in, managed different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = False options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_016(self): """ Test comparison of two identical objects, all attributes filled in, managedOnly different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = False options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = False options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_017(self): """ Test comparison of two identical objects, all attributes filled in, diagnostics different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.config = "config" options1.full = True options1.managed = True options1.managedOnly = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = 0o631 options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = False options1.actions = [ "collect", ] options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.config = "config" options2.full = True options2.managed = True options2.managedOnly = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = 0o631 options2.output = True options2.debug = True options2.stacktrace = False options2.diagnostics = True options2.actions = [ "collect", ] self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) ########################### # Test buildArgumentList() ########################### def testBuildArgumentList_001(self): """Test with no values set, validate=False.""" options = Options() argumentList = options.buildArgumentList(validate=False) self.assertEqual([], argumentList) def testBuildArgumentList_002(self): """Test with help set, validate=False.""" options = Options() options.help = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--help"], argumentList) def testBuildArgumentList_003(self): """Test with version set, validate=False.""" options = Options() options.version = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--version"], argumentList) def testBuildArgumentList_004(self): """Test with verbose set, validate=False.""" options = Options() options.verbose = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--verbose"], argumentList) def testBuildArgumentList_005(self): """Test with quiet set, validate=False.""" options = Options() options.quiet = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--quiet"], argumentList) def testBuildArgumentList_006(self): """Test with config set, validate=False.""" options = Options() options.config = "stuff" argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--config", "stuff"], argumentList) def testBuildArgumentList_007(self): """Test with full set, validate=False.""" options = Options() options.full = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--full"], argumentList) def testBuildArgumentList_008(self): """Test with logfile set, validate=False.""" options = Options() options.logfile = "bogus" argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--logfile", "bogus"], argumentList) def testBuildArgumentList_009(self): """Test with owner set, validate=False.""" options = Options() options.owner = ("ken", "group") argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--owner", "ken:group"], argumentList) def testBuildArgumentList_010(self): """Test with mode set, validate=False.""" options = Options() options.mode = 0o644 argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--mode", "644"], argumentList) def testBuildArgumentList_011(self): """Test with output set, validate=False.""" options = Options() options.output = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--output"], argumentList) def testBuildArgumentList_012(self): """Test with debug set, validate=False.""" options = Options() options.debug = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--debug"], argumentList) def testBuildArgumentList_013(self): """Test with stacktrace set, validate=False.""" options = Options() options.stacktrace = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--stack"], argumentList) def testBuildArgumentList_014(self): """Test with actions containing one item, validate=False.""" options = Options() options.actions = [ "collect", ] argumentList = options.buildArgumentList(validate=False) self.assertEqual(["collect"], argumentList) def testBuildArgumentList_015(self): """Test with actions containing multiple items, validate=False.""" options = Options() options.actions = [ "collect", "stage", "store", "purge", ] argumentList = options.buildArgumentList(validate=False) self.assertEqual(["collect", "stage", "store", "purge"], argumentList) def testBuildArgumentList_016(self): """Test with all values set, actions containing one item, validate=False.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.managed = True options.managedOnly = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", ] argumentList = options.buildArgumentList(validate=False) self.assertEqual( [ "--help", "--version", "--verbose", "--quiet", "--config", "config", "--full", "--managed", "--managed-only", "--logfile", "logfile", "--owner", "a:b", "--mode", "631", "--output", "--debug", "--stack", "--diagnostics", "collect", ], argumentList, ) def testBuildArgumentList_017(self): """Test with all values set, actions containing multiple items, validate=False.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.managed = True options.managedOnly = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", "stage", ] argumentList = options.buildArgumentList(validate=False) self.assertEqual( [ "--help", "--version", "--verbose", "--quiet", "--config", "config", "--full", "--managed", "--managed-only", "--logfile", "logfile", "--owner", "a:b", "--mode", "631", "--output", "--debug", "--stack", "--diagnostics", "collect", "stage", ], argumentList, ) def testBuildArgumentList_018(self): """Test with no values set, validate=True.""" options = Options() self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_019(self): """Test with help set, validate=True.""" options = Options() options.help = True argumentList = options.buildArgumentList(validate=True) self.assertEqual(["--help"], argumentList) def testBuildArgumentList_020(self): """Test with version set, validate=True.""" options = Options() options.version = True argumentList = options.buildArgumentList(validate=True) self.assertEqual(["--version"], argumentList) def testBuildArgumentList_021(self): """Test with verbose set, validate=True.""" options = Options() options.verbose = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_022(self): """Test with quiet set, validate=True.""" options = Options() options.quiet = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_023(self): """Test with config set, validate=True.""" options = Options() options.config = "stuff" self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_024(self): """Test with full set, validate=True.""" options = Options() options.full = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_025(self): """Test with logfile set, validate=True.""" options = Options() options.logfile = "bogus" self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_026(self): """Test with owner set, validate=True.""" options = Options() options.owner = ("ken", "group") self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_027(self): """Test with mode set, validate=True.""" options = Options() options.mode = 0o644 self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_028(self): """Test with output set, validate=True.""" options = Options() options.output = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_029(self): """Test with debug set, validate=True.""" options = Options() options.debug = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_030(self): """Test with stacktrace set, validate=True.""" options = Options() options.stacktrace = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_031(self): """Test with actions containing one item, validate=True.""" options = Options() options.actions = [ "collect", ] argumentList = options.buildArgumentList(validate=True) self.assertEqual(["collect"], argumentList) def testBuildArgumentList_032(self): """Test with actions containing multiple items, validate=True.""" options = Options() options.actions = [ "collect", "stage", "store", "purge", ] argumentList = options.buildArgumentList(validate=True) self.assertEqual(["collect", "stage", "store", "purge"], argumentList) def testBuildArgumentList_033(self): """Test with all values set (except managed ones), actions containing one item, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", ] argumentList = options.buildArgumentList(validate=True) self.assertEqual( [ "--help", "--version", "--verbose", "--quiet", "--config", "config", "--full", "--logfile", "logfile", "--owner", "a:b", "--mode", "631", "--output", "--debug", "--stack", "--diagnostics", "collect", ], argumentList, ) def testBuildArgumentList_034(self): """Test with all values set (except managed ones), actions containing multiple items, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", "stage", ] argumentList = options.buildArgumentList(validate=True) self.assertEqual( [ "--help", "--version", "--verbose", "--quiet", "--config", "config", "--full", "--logfile", "logfile", "--owner", "a:b", "--mode", "631", "--output", "--debug", "--stack", "--diagnostics", "collect", "stage", ], argumentList, ) def testBuildArgumentList_035(self): """Test with managed set, validate=False.""" options = Options() options.managed = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--managed"], argumentList) def testBuildArgumentList_036(self): """Test with managed set, validate=True.""" options = Options() options.managed = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_037(self): """Test with managedOnly set, validate=False.""" options = Options() options.managedOnly = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--managed-only"], argumentList) def testBuildArgumentList_038(self): """Test with managedOnly set, validate=True.""" options = Options() options.managedOnly = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_039(self): """Test with all values set, actions containing one item, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.managed = True options.managedOnly = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", ] self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_040(self): """Test with all values set, actions containing multiple items, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.managed = True options.managedOnly = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", "stage", ] self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_041(self): """Test with diagnostics set, validate=False.""" options = Options() options.diagnostics = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--diagnostics"], argumentList) def testBuildArgumentList_042(self): """Test with diagnostics set, validate=True.""" options = Options() options.diagnostics = True argumentList = options.buildArgumentList(validate=True) self.assertEqual(["--diagnostics"], argumentList) ############################# # Test buildArgumentString() ############################# def testBuildArgumentString_001(self): """Test with no values set, validate=False.""" options = Options() argumentString = options.buildArgumentString(validate=False) self.assertEqual("", argumentString) def testBuildArgumentString_002(self): """Test with help set, validate=False.""" options = Options() options.help = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--help ", argumentString) def testBuildArgumentString_003(self): """Test with version set, validate=False.""" options = Options() options.version = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--version ", argumentString) def testBuildArgumentString_004(self): """Test with verbose set, validate=False.""" options = Options() options.verbose = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--verbose ", argumentString) def testBuildArgumentString_005(self): """Test with quiet set, validate=False.""" options = Options() options.quiet = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--quiet ", argumentString) def testBuildArgumentString_006(self): """Test with config set, validate=False.""" options = Options() options.config = "stuff" argumentString = options.buildArgumentString(validate=False) self.assertEqual('--config "stuff" ', argumentString) def testBuildArgumentString_007(self): """Test with full set, validate=False.""" options = Options() options.full = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--full ", argumentString) def testBuildArgumentString_008(self): """Test with logfile set, validate=False.""" options = Options() options.logfile = "bogus" argumentString = options.buildArgumentString(validate=False) self.assertEqual('--logfile "bogus" ', argumentString) def testBuildArgumentString_009(self): """Test with owner set, validate=False.""" options = Options() options.owner = ("ken", "group") argumentString = options.buildArgumentString(validate=False) self.assertEqual('--owner "ken:group" ', argumentString) def testBuildArgumentString_010(self): """Test with mode set, validate=False.""" options = Options() options.mode = 0o644 argumentString = options.buildArgumentString(validate=False) self.assertEqual("--mode 644 ", argumentString) def testBuildArgumentString_011(self): """Test with output set, validate=False.""" options = Options() options.output = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--output ", argumentString) def testBuildArgumentString_012(self): """Test with debug set, validate=False.""" options = Options() options.debug = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--debug ", argumentString) def testBuildArgumentString_013(self): """Test with stacktrace set, validate=False.""" options = Options() options.stacktrace = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--stack ", argumentString) def testBuildArgumentString_014(self): """Test with actions containing one item, validate=False.""" options = Options() options.actions = [ "collect", ] argumentString = options.buildArgumentString(validate=False) self.assertEqual('"collect" ', argumentString) def testBuildArgumentString_015(self): """Test with actions containing multiple items, validate=False.""" options = Options() options.actions = [ "collect", "stage", "store", "purge", ] argumentString = options.buildArgumentString(validate=False) self.assertEqual('"collect" "stage" "store" "purge" ', argumentString) def testBuildArgumentString_016(self): """Test with all values set, actions containing one item, validate=False.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.managed = True options.managedOnly = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", ] argumentString = options.buildArgumentString(validate=False) self.assertEqual( '--help --version --verbose --quiet --config "config" --full --managed --managed-only --logfile "logfile" --owner "a:b" --mode 631 --output --debug --stack --diagnostics "collect" ', argumentString, ) def testBuildArgumentString_017(self): """Test with all values set, actions containing multiple items, validate=False.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", "stage", ] argumentString = options.buildArgumentString(validate=False) self.assertEqual( '--help --version --verbose --quiet --config "config" --full --logfile "logfile" --owner "a:b" --mode 631 --output --debug --stack --diagnostics "collect" "stage" ', argumentString, ) def testBuildArgumentString_018(self): """Test with no values set, validate=True.""" options = Options() self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_019(self): """Test with help set, validate=True.""" options = Options() options.help = True argumentString = options.buildArgumentString(validate=True) self.assertEqual("--help ", argumentString) def testBuildArgumentString_020(self): """Test with version set, validate=True.""" options = Options() options.version = True argumentString = options.buildArgumentString(validate=True) self.assertEqual("--version ", argumentString) def testBuildArgumentString_021(self): """Test with verbose set, validate=True.""" options = Options() options.verbose = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_022(self): """Test with quiet set, validate=True.""" options = Options() options.quiet = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_023(self): """Test with config set, validate=True.""" options = Options() options.config = "stuff" self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_024(self): """Test with full set, validate=True.""" options = Options() options.full = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_025(self): """Test with logfile set, validate=True.""" options = Options() options.logfile = "bogus" self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_026(self): """Test with owner set, validate=True.""" options = Options() options.owner = ("ken", "group") self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_027(self): """Test with mode set, validate=True.""" options = Options() options.mode = 0o644 self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_028(self): """Test with output set, validate=True.""" options = Options() options.output = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_029(self): """Test with debug set, validate=True.""" options = Options() options.debug = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_030(self): """Test with stacktrace set, validate=True.""" options = Options() options.stacktrace = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_031(self): """Test with actions containing one item, validate=True.""" options = Options() options.actions = [ "collect", ] argumentString = options.buildArgumentString(validate=True) self.assertEqual('"collect" ', argumentString) def testBuildArgumentString_032(self): """Test with actions containing multiple items, validate=True.""" options = Options() options.actions = [ "collect", "stage", "store", "purge", ] argumentString = options.buildArgumentString(validate=True) self.assertEqual('"collect" "stage" "store" "purge" ', argumentString) def testBuildArgumentString_033(self): """Test with all values set (except managed ones), actions containing one item, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", ] argumentString = options.buildArgumentString(validate=True) self.assertEqual( '--help --version --verbose --quiet --config "config" --full --logfile "logfile" --owner "a:b" --mode 631 --output --debug --stack --diagnostics "collect" ', argumentString, ) def testBuildArgumentString_034(self): """Test with all values set (except managed ones), actions containing multiple items, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", "stage", ] argumentString = options.buildArgumentString(validate=True) self.assertEqual( '--help --version --verbose --quiet --config "config" --full --logfile "logfile" --owner "a:b" --mode 631 --output --debug --stack --diagnostics "collect" "stage" ', argumentString, ) def testBuildArgumentString_035(self): """Test with managed set, validate=False.""" options = Options() options.managed = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--managed ", argumentString) def testBuildArgumentString_036(self): """Test with managed set, validate=True.""" options = Options() options.managed = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_037(self): """Test with full set, validate=False.""" options = Options() options.managedOnly = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--managed-only ", argumentString) def testBuildArgumentString_038(self): """Test with managedOnly set, validate=True.""" options = Options() options.managedOnly = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_039(self): """Test with all values set (except managed ones), actions containing one item, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.managed = True options.managedOnly = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", ] self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_040(self): """Test with all values set (except managed ones), actions containing multiple items, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.config = "config" options.full = True options.managed = True options.managedOnly = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.actions = [ "collect", "stage", ] self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_041(self): """Test with diagnostics set, validate=False.""" options = Options() options.diagnostics = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--diagnostics ", argumentString) def testBuildArgumentString_042(self): """Test with diagnostics set, validate=True.""" options = Options() options.diagnostics = True argumentString = options.buildArgumentString(validate=True) self.assertEqual("--diagnostics ", argumentString) ###################### # TestActionSet class ###################### class TestActionSet(unittest.TestCase): """Tests for the _ActionSet class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ####################################### # Test constructor, "index" order mode ####################################### def testActionSet_001(self): """ Test with actions=None, extensions=None. """ actions = None extensions = ExtensionsConfig(None, None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_002(self): """ Test with actions=[], extensions=None. """ actions = [] extensions = ExtensionsConfig(None, None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_003(self): """ Test with actions=[], extensions=[]. """ actions = [] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_004(self): """ Test with actions=[ collect ], extensions=[]. """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testActionSet_005(self): """ Test with actions=[ stage ], extensions=[]. """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) def testActionSet_006(self): """ Test with actions=[ store ], extensions=[]. """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) def testActionSet_007(self): """ Test with actions=[ purge ], extensions=[]. """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executePurge, actionSet.actionSet[0].function) def testActionSet_008(self): """ Test with actions=[ all ], extensions=[]. """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testActionSet_009(self): """ Test with actions=[ rebuild ], extensions=[]. """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("rebuild", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeRebuild, actionSet.actionSet[0].function) def testActionSet_010(self): """ Test with actions=[ validate ], extensions=[]. """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("validate", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeValidate, actionSet.actionSet[0].function) def testActionSet_011(self): """ Test with actions=[ collect, collect ], extensions=[]. """ actions = [ "collect", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_012(self): """ Test with actions=[ collect, stage ], extensions=[]. """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testActionSet_013(self): """ Test with actions=[ collect, store ], extensions=[]. """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testActionSet_014(self): """ Test with actions=[ collect, purge ], extensions=[]. """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_015(self): """ Test with actions=[ collect, all ], extensions=[]. """ actions = [ "collect", "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_016(self): """ Test with actions=[ collect, rebuild ], extensions=[]. """ actions = [ "collect", "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_017(self): """ Test with actions=[ collect, validate ], extensions=[]. """ actions = [ "collect", "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_018(self): """ Test with actions=[ stage, collect ], extensions=[]. """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testActionSet_019(self): """ Test with actions=[ stage, stage ], extensions=[]. """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testActionSet_020(self): """ Test with actions=[ stage, store ], extensions=[]. """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testActionSet_021(self): """ Test with actions=[ stage, purge ], extensions=[]. """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_022(self): """ Test with actions=[ stage, all ], extensions=[]. """ actions = [ "stage", "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_023(self): """ Test with actions=[ stage, rebuild ], extensions=[]. """ actions = [ "stage", "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_024(self): """ Test with actions=[ stage, validate ], extensions=[]. """ actions = [ "stage", "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_025(self): """ Test with actions=[ store, collect ], extensions=[]. """ actions = [ "store", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testActionSet_026(self): """ Test with actions=[ store, stage ], extensions=[]. """ actions = [ "store", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testActionSet_027(self): """ Test with actions=[ store, store ], extensions=[]. """ actions = [ "store", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testActionSet_028(self): """ Test with actions=[ store, purge ], extensions=[]. """ actions = [ "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_029(self): """ Test with actions=[ store, all ], extensions=[]. """ actions = [ "store", "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_030(self): """ Test with actions=[ store, rebuild ], extensions=[]. """ actions = [ "store", "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_031(self): """ Test with actions=[ store, validate ], extensions=[]. """ actions = [ "store", "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_032(self): """ Test with actions=[ purge, collect ], extensions=[]. """ actions = [ "purge", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_033(self): """ Test with actions=[ purge, stage ], extensions=[]. """ actions = [ "purge", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_034(self): """ Test with actions=[ purge, store ], extensions=[]. """ actions = [ "purge", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_035(self): """ Test with actions=[ purge, purge ], extensions=[]. """ actions = [ "purge", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executePurge, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_036(self): """ Test with actions=[ purge, all ], extensions=[]. """ actions = [ "purge", "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_037(self): """ Test with actions=[ purge, rebuild ], extensions=[]. """ actions = [ "purge", "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_038(self): """ Test with actions=[ purge, validate ], extensions=[]. """ actions = [ "purge", "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_039(self): """ Test with actions=[ all, collect ], extensions=[]. """ actions = [ "all", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_040(self): """ Test with actions=[ all, stage ], extensions=[]. """ actions = [ "all", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_041(self): """ Test with actions=[ all, store ], extensions=[]. """ actions = [ "all", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_042(self): """ Test with actions=[ all, purge ], extensions=[]. """ actions = [ "all", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_043(self): """ Test with actions=[ all, all ], extensions=[]. """ actions = [ "all", "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_044(self): """ Test with actions=[ all, rebuild ], extensions=[]. """ actions = [ "all", "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_045(self): """ Test with actions=[ all, validate ], extensions=[]. """ actions = [ "all", "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_046(self): """ Test with actions=[ rebuild, collect ], extensions=[]. """ actions = [ "rebuild", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_047(self): """ Test with actions=[ rebuild, stage ], extensions=[]. """ actions = [ "rebuild", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_048(self): """ Test with actions=[ rebuild, store ], extensions=[]. """ actions = [ "rebuild", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_049(self): """ Test with actions=[ rebuild, purge ], extensions=[]. """ actions = [ "rebuild", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_050(self): """ Test with actions=[ rebuild, all ], extensions=[]. """ actions = [ "rebuild", "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_051(self): """ Test with actions=[ rebuild, rebuild ], extensions=[]. """ actions = [ "rebuild", "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_052(self): """ Test with actions=[ rebuild, validate ], extensions=[]. """ actions = [ "rebuild", "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_053(self): """ Test with actions=[ validate, collect ], extensions=[]. """ actions = [ "validate", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_054(self): """ Test with actions=[ validate, stage ], extensions=[]. """ actions = [ "validate", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_055(self): """ Test with actions=[ validate, store ], extensions=[]. """ actions = [ "validate", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_056(self): """ Test with actions=[ validate, purge ], extensions=[]. """ actions = [ "validate", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_057(self): """ Test with actions=[ validate, all ], extensions=[]. """ actions = [ "validate", "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_058(self): """ Test with actions=[ validate, rebuild ], extensions=[]. """ actions = [ "validate", "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_059(self): """ Test with actions=[ validate, validate ], extensions=[]. """ actions = [ "validate", "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_060(self): """ Test with actions=[ bogus ], extensions=[]. """ actions = [ "bogus", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_061(self): """ Test with actions=[ bogus, collect ], extensions=[]. """ actions = [ "bogus", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_062(self): """ Test with actions=[ bogus, stage ], extensions=[]. """ actions = [ "bogus", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_063(self): """ Test with actions=[ bogus, store ], extensions=[]. """ actions = [ "bogus", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_064(self): """ Test with actions=[ bogus, purge ], extensions=[]. """ actions = [ "bogus", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_065(self): """ Test with actions=[ bogus, all ], extensions=[]. """ actions = [ "bogus", "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_066(self): """ Test with actions=[ bogus, rebuild ], extensions=[]. """ actions = [ "bogus", "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_067(self): """ Test with actions=[ bogus, validate ], extensions=[]. """ actions = [ "bogus", "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_068(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ]. """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_069(self): """ Test with actions=[ stage, one ], extensions=[ (one, index 50) ]. """ actions = [ "stage", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testActionSet_070(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ]. """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testActionSet_071(self): """ Test with actions=[ purge, one ], extensions=[ (one, index 50) ]. """ actions = [ "purge", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_072(self): """ Test with actions=[ all, one ], extensions=[ (one, index 50) ]. """ actions = [ "all", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_073(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, index 50) ]. """ actions = [ "rebuild", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_074(self): """ Test with actions=[ validate, one ], extensions=[ (one, index 50) ]. """ actions = [ "validate", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_075(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ]. """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(150, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_076(self): """ Test with actions=[ stage, one ], extensions=[ (one, index 150) ]. """ actions = [ "stage", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testActionSet_077(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ]. """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testActionSet_078(self): """ Test with actions=[ purge, one ], extensions=[ (one, index 150) ]. """ actions = [ "purge", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_079(self): """ Test with actions=[ all, one ], extensions=[ (one, index 150) ]. """ actions = [ "all", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_080(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, index 150) ]. """ actions = [ "rebuild", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_081(self): """ Test with actions=[ validate, one ], extensions=[ (one, index 150) ]. """ actions = [ "validate", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_082(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 250) ]. """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 250)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(250, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_083(self): """ Test with actions=[ stage, one ], extensions=[ (one, index 250) ]. """ actions = [ "stage", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 250)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(250, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_084(self): """ Test with actions=[ store, one ], extensions=[ (one, index 250) ]. """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 250)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(250, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testActionSet_085(self): """ Test with actions=[ purge, one ], extensions=[ (one, index 250) ]. """ actions = [ "purge", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 250)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(250, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_086(self): """ Test with actions=[ all, one ], extensions=[ (one, index 250) ]. """ actions = [ "all", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 250)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_087(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, index 250) ]. """ actions = [ "rebuild", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 250)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_088(self): """ Test with actions=[ validate, one ], extensions=[ (one, index 250) ]. """ actions = [ "validate", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 250)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_089(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 350) ]. """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 350)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(350, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_090(self): """ Test with actions=[ stage, one ], extensions=[ (one, index 350) ]. """ actions = [ "stage", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 350)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(350, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_091(self): """ Test with actions=[ store, one ], extensions=[ (one, index 350) ]. """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 350)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) self.assertEqual(350, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_092(self): """ Test with actions=[ purge, one ], extensions=[ (one, index 350) ]. """ actions = [ "purge", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 350)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(350, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testActionSet_093(self): """ Test with actions=[ all, one ], extensions=[ (one, index 350) ]. """ actions = [ "all", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 350)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_094(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, index 350) ]. """ actions = [ "rebuild", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 350)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_095(self): """ Test with actions=[ validate, one ], extensions=[ (one, index 350) ]. """ actions = [ "validate", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 350)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_096(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 450) ]. """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 450)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(450, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_097(self): """ Test with actions=[ stage, one ], extensions=[ (one, index 450) ]. """ actions = [ "stage", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 450)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(450, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_098(self): """ Test with actions=[ store, one ], extensions=[ (one, index 450) ]. """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 450)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) self.assertEqual(450, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_099(self): """ Test with actions=[ purge, one ], extensions=[ (one, index 450) ]. """ actions = [ "purge", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 450)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executePurge, actionSet.actionSet[0].function) self.assertEqual(450, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_100(self): """ Test with actions=[ all, one ], extensions=[ (one, index 450) ]. """ actions = [ "all", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 450)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_101(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, index 450) ]. """ actions = [ "rebuild", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 450)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_102(self): """ Test with actions=[ validate, one ], extensions=[ (one, index 450) ]. """ actions = [ "validate", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 450)], None) options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testActionSet_103(self): """ Test with actions=[ one, one ], extensions=[ (one, index 450) ]. """ actions = [ "one", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 450)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(450, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(450, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testActionSet_104(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[]. """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testActionSet_105(self): """ Test with actions=[ stage, purge, collect, store ], extensions=[]. """ actions = [ "stage", "purge", "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testActionSet_106(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)]. """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 9) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) self.assertEqual(150, actionSet.actionSet[2].index) self.assertEqual("two", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(isfile, actionSet.actionSet[2].function) self.assertEqual(200, actionSet.actionSet[3].index) self.assertEqual("stage", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executeStage, actionSet.actionSet[3].function) self.assertEqual(250, actionSet.actionSet[4].index) self.assertEqual("three", actionSet.actionSet[4].name) self.assertEqual(None, actionSet.actionSet[4].preHooks) self.assertEqual(None, actionSet.actionSet[4].postHooks) self.assertEqual(islink, actionSet.actionSet[4].function) self.assertEqual(300, actionSet.actionSet[5].index) self.assertEqual("store", actionSet.actionSet[5].name) self.assertEqual(None, actionSet.actionSet[5].preHooks) self.assertEqual(None, actionSet.actionSet[5].postHooks) self.assertEqual(executeStore, actionSet.actionSet[5].function) self.assertEqual(350, actionSet.actionSet[6].index) self.assertEqual("four", actionSet.actionSet[6].name) self.assertEqual(None, actionSet.actionSet[6].preHooks) self.assertEqual(None, actionSet.actionSet[6].postHooks) self.assertEqual(isabs, actionSet.actionSet[6].function) self.assertEqual(400, actionSet.actionSet[7].index) self.assertEqual("purge", actionSet.actionSet[7].name) self.assertEqual(None, actionSet.actionSet[7].preHooks) self.assertEqual(None, actionSet.actionSet[7].postHooks) self.assertEqual(executePurge, actionSet.actionSet[7].function) self.assertEqual(450, actionSet.actionSet[8].index) self.assertEqual("five", actionSet.actionSet[8].name) self.assertEqual(None, actionSet.actionSet[8].preHooks) self.assertEqual(None, actionSet.actionSet[8].postHooks) self.assertEqual(exists, actionSet.actionSet[8].function) def testActionSet_107(self): """ Test with actions=[ one, five, collect, store, three, stage, four, purge, two ], extensions=[ (index 50, 150, 250, 350, 450)]. """ actions = [ "one", "five", "collect", "store", "three", "stage", "four", "purge", "two", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 9) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) self.assertEqual(150, actionSet.actionSet[2].index) self.assertEqual("two", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(isfile, actionSet.actionSet[2].function) self.assertEqual(200, actionSet.actionSet[3].index) self.assertEqual("stage", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executeStage, actionSet.actionSet[3].function) self.assertEqual(250, actionSet.actionSet[4].index) self.assertEqual("three", actionSet.actionSet[4].name) self.assertEqual(None, actionSet.actionSet[4].preHooks) self.assertEqual(None, actionSet.actionSet[4].postHooks) self.assertEqual(islink, actionSet.actionSet[4].function) self.assertEqual(300, actionSet.actionSet[5].index) self.assertEqual("store", actionSet.actionSet[5].name) self.assertEqual(None, actionSet.actionSet[5].preHooks) self.assertEqual(None, actionSet.actionSet[5].postHooks) self.assertEqual(executeStore, actionSet.actionSet[5].function) self.assertEqual(350, actionSet.actionSet[6].index) self.assertEqual("four", actionSet.actionSet[6].name) self.assertEqual(None, actionSet.actionSet[6].preHooks) self.assertEqual(None, actionSet.actionSet[6].postHooks) self.assertEqual(isabs, actionSet.actionSet[6].function) self.assertEqual(400, actionSet.actionSet[7].index) self.assertEqual("purge", actionSet.actionSet[7].name) self.assertEqual(None, actionSet.actionSet[7].preHooks) self.assertEqual(None, actionSet.actionSet[7].postHooks) self.assertEqual(executePurge, actionSet.actionSet[7].function) self.assertEqual(450, actionSet.actionSet[8].index) self.assertEqual("five", actionSet.actionSet[8].name) self.assertEqual(None, actionSet.actionSet[8].preHooks) self.assertEqual(None, actionSet.actionSet[8].postHooks) self.assertEqual(exists, actionSet.actionSet[8].function) def testActionSet_108(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ]. """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testActionSet_109(self): """ Test with actions=[ collect ], extensions=[], hooks=[] """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.hooks = [] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testActionSet_110(self): """ Test with actions=[ collect ], extensions=[], pre-hook on 'stage' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.hooks = [PreActionHook("stage", "something")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testActionSet_111(self): """ Test with actions=[ collect ], extensions=[], post-hook on 'stage' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.hooks = [PostActionHook("stage", "something")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testActionSet_112(self): """ Test with actions=[ collect ], extensions=[], pre-hook on 'collect' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.hooks = [PreActionHook("collect", "something")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("collect", "something")], actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testActionSet_113(self): """ Test with actions=[ collect ], extensions=[], post-hook on 'collect' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.hooks = [PostActionHook("collect", "something")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("collect", "something")], actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testActionSet_114(self): """ Test with actions=[ collect ], extensions=[], pre- and post-hook on 'collect' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.hooks = [PreActionHook("collect", "something1"), PostActionHook("collect", "something2")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("collect", "something1")], actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("collect", "something2")], actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testActionSet_115(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], hooks=[] """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testActionSet_116(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], pre-hook on "store" action. """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PreActionHook("store", "whatever"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testActionSet_117(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], post-hook on "store" action. """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PostActionHook("store", "whatever"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testActionSet_118(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], pre-hook on "one" action. """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PreActionHook("one", "extension"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("one", "extension")], actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testActionSet_119(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], post-hook on "one" action. """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testActionSet_120(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], pre- and post-hook on "one" action. """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension2"), PreActionHook("one", "extension1"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("one", "extension1")], actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension2")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testActionSet_121(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], hooks=[] """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_122(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], pre-hook on "purge" action """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PreActionHook("purge", "rm -f"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_123(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], post-hook on "purge" action """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PostActionHook("purge", "rm -f"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_124(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], pre-hook on "collect" action """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PreActionHook("collect", "something"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual([PreActionHook("collect", "something")], actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_125(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], post-hook on "collect" action """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PostActionHook("collect", "something"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual([PostActionHook("collect", "something")], actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_126(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], pre-hook on "one" action """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PreActionHook("one", "extension"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("one", "extension")], actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_127(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], post-hook on "one" action """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_128(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], set of various pre- and post hooks. """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension"), PreActionHook("collect", "something1"), PreActionHook("collect", "something2"), PostActionHook("stage", "whatever1"), PostActionHook("stage", "whatever2"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual( [PreActionHook("collect", "something1"), PreActionHook("collect", "something2")], actionSet.actionSet[1].preHooks ) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testActionSet_129(self): """ Test with actions=[ stage, one ], extensions=[ (one, index 50) ], set of various pre- and post hooks. """ actions = [ "stage", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension"), PreActionHook("collect", "something1"), PreActionHook("collect", "something2"), PostActionHook("stage", "whatever1"), PostActionHook("stage", "whatever2"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual( [PostActionHook("stage", "whatever1"), PostActionHook("stage", "whatever2")], actionSet.actionSet[1].postHooks ) self.assertEqual(executeStage, actionSet.actionSet[1].function) ############################################ # Test constructor, "dependency" order mode ############################################ def testDependencyMode_001(self): """ Test with actions=None, extensions=None. """ actions = None extensions = ExtensionsConfig(None, "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_002(self): """ Test with actions=[], extensions=None. """ actions = [] extensions = ExtensionsConfig(None, "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_003(self): """ Test with actions=[], extensions=[]. """ actions = [] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_004(self): """ Test with actions=[ collect ], extensions=[]. """ actions = [ "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_005(self): """ Test with actions=[ stage ], extensions=[]. """ actions = [ "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) def testDependencyMode_006(self): """ Test with actions=[ store ], extensions=[]. """ actions = [ "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) def testDependencyMode_007(self): """ Test with actions=[ purge ], extensions=[]. """ actions = [ "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executePurge, actionSet.actionSet[0].function) def testDependencyMode_008(self): """ Test with actions=[ all ], extensions=[]. """ actions = [ "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testDependencyMode_009(self): """ Test with actions=[ rebuild ], extensions=[]. """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("rebuild", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeRebuild, actionSet.actionSet[0].function) def testDependencyMode_010(self): """ Test with actions=[ validate ], extensions=[]. """ actions = [ "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("validate", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeValidate, actionSet.actionSet[0].function) def testDependencyMode_011(self): """ Test with actions=[ collect, collect ], extensions=[]. """ actions = [ "collect", "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_012(self): """ Test with actions=[ collect, stage ], extensions=[]. """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_013(self): """ Test with actions=[ collect, store ], extensions=[]. """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_014(self): """ Test with actions=[ collect, purge ], extensions=[]. """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_015(self): """ Test with actions=[ collect, all ], extensions=[]. """ actions = [ "collect", "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_016(self): """ Test with actions=[ collect, rebuild ], extensions=[]. """ actions = [ "collect", "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_017(self): """ Test with actions=[ collect, validate ], extensions=[]. """ actions = [ "collect", "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_018(self): """ Test with actions=[ stage, collect ], extensions=[]. """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_019(self): """ Test with actions=[ stage, stage ], extensions=[]. """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_020(self): """ Test with actions=[ stage, store ], extensions=[]. """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_021(self): """ Test with actions=[ stage, purge ], extensions=[]. """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_022(self): """ Test with actions=[ stage, all ], extensions=[]. """ actions = [ "stage", "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_023(self): """ Test with actions=[ stage, rebuild ], extensions=[]. """ actions = [ "stage", "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_024(self): """ Test with actions=[ stage, validate ], extensions=[]. """ actions = [ "stage", "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_025(self): """ Test with actions=[ store, collect ], extensions=[]. """ actions = [ "store", "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_026(self): """ Test with actions=[ store, stage ], extensions=[]. """ actions = [ "store", "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_027(self): """ Test with actions=[ store, store ], extensions=[]. """ actions = [ "store", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_028(self): """ Test with actions=[ store, purge ], extensions=[]. """ actions = [ "store", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_029(self): """ Test with actions=[ store, all ], extensions=[]. """ actions = [ "store", "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_030(self): """ Test with actions=[ store, rebuild ], extensions=[]. """ actions = [ "store", "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_031(self): """ Test with actions=[ store, validate ], extensions=[]. """ actions = [ "store", "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_032(self): """ Test with actions=[ purge, collect ], extensions=[]. """ actions = [ "purge", "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_033(self): """ Test with actions=[ purge, stage ], extensions=[]. """ actions = [ "purge", "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_034(self): """ Test with actions=[ purge, store ], extensions=[]. """ actions = [ "purge", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_035(self): """ Test with actions=[ purge, purge ], extensions=[]. """ actions = [ "purge", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executePurge, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_036(self): """ Test with actions=[ purge, all ], extensions=[]. """ actions = [ "purge", "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_037(self): """ Test with actions=[ purge, rebuild ], extensions=[]. """ actions = [ "purge", "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_038(self): """ Test with actions=[ purge, validate ], extensions=[]. """ actions = [ "purge", "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_039(self): """ Test with actions=[ all, collect ], extensions=[]. """ actions = [ "all", "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_040(self): """ Test with actions=[ all, stage ], extensions=[]. """ actions = [ "all", "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_041(self): """ Test with actions=[ all, store ], extensions=[]. """ actions = [ "all", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_042(self): """ Test with actions=[ all, purge ], extensions=[]. """ actions = [ "all", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_043(self): """ Test with actions=[ all, all ], extensions=[]. """ actions = [ "all", "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_044(self): """ Test with actions=[ all, rebuild ], extensions=[]. """ actions = [ "all", "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_045(self): """ Test with actions=[ all, validate ], extensions=[]. """ actions = [ "all", "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_046(self): """ Test with actions=[ rebuild, collect ], extensions=[]. """ actions = [ "rebuild", "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_047(self): """ Test with actions=[ rebuild, stage ], extensions=[]. """ actions = [ "rebuild", "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_048(self): """ Test with actions=[ rebuild, store ], extensions=[]. """ actions = [ "rebuild", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_049(self): """ Test with actions=[ rebuild, purge ], extensions=[]. """ actions = [ "rebuild", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_050(self): """ Test with actions=[ rebuild, all ], extensions=[]. """ actions = [ "rebuild", "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_051(self): """ Test with actions=[ rebuild, rebuild ], extensions=[]. """ actions = [ "rebuild", "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_052(self): """ Test with actions=[ rebuild, validate ], extensions=[]. """ actions = [ "rebuild", "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_053(self): """ Test with actions=[ validate, collect ], extensions=[]. """ actions = [ "validate", "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_054(self): """ Test with actions=[ validate, stage ], extensions=[]. """ actions = [ "validate", "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_055(self): """ Test with actions=[ validate, store ], extensions=[]. """ actions = [ "validate", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_056(self): """ Test with actions=[ validate, purge ], extensions=[]. """ actions = [ "validate", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_057(self): """ Test with actions=[ validate, all ], extensions=[]. """ actions = [ "validate", "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_058(self): """ Test with actions=[ validate, rebuild ], extensions=[]. """ actions = [ "validate", "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_059(self): """ Test with actions=[ validate, validate ], extensions=[]. """ actions = [ "validate", "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_060(self): """ Test with actions=[ bogus ], extensions=[]. """ actions = [ "bogus", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_061(self): """ Test with actions=[ bogus, collect ], extensions=[]. """ actions = [ "bogus", "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_062(self): """ Test with actions=[ bogus, stage ], extensions=[]. """ actions = [ "bogus", "stage", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_063(self): """ Test with actions=[ bogus, store ], extensions=[]. """ actions = [ "bogus", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_064(self): """ Test with actions=[ bogus, purge ], extensions=[]. """ actions = [ "bogus", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_065(self): """ Test with actions=[ bogus, all ], extensions=[]. """ actions = [ "bogus", "all", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_066(self): """ Test with actions=[ bogus, rebuild ], extensions=[]. """ actions = [ "bogus", "rebuild", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_067(self): """ Test with actions=[ bogus, validate ], extensions=[]. """ actions = [ "bogus", "validate", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_068(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ]. """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_069(self): """ Test with actions=[ stage, one ], extensions=[ (one, before stage) ]. """ actions = [ "stage", "one", ] dependencies = ActionDependencies(["stage"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_070(self): """ Test with actions=[ store, one ], extensions=[ (one, before store) ]. """ actions = [ "store", "one", ] dependencies = ActionDependencies(["store"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_071(self): """ Test with actions=[ purge, one ], extensions=[ (one, before purge) ]. """ actions = [ "purge", "one", ] dependencies = ActionDependencies(["purge"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_072(self): """ Test with actions=[ all, one ], extensions=[ (one, before collect) ]. """ actions = [ "all", "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_073(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, before collect) ]. """ actions = [ "rebuild", "one", ] dependencies = ActionDependencies(["collect"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_074(self): """ Test with actions=[ validate, one ], extensions=[ (one, before collect) ]. """ actions = [ "validate", "one", ] dependencies = ActionDependencies(["stage"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_075(self): """ Test with actions=[ collect, one ], extensions=[ (one, after collect) ]. """ actions = [ "collect", "one", ] dependencies = ActionDependencies([], ["collect"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testDependencyMode_076(self): """ Test with actions=[ stage, one ], extensions=[ (one, after collect) ]. """ actions = [ "stage", "one", ] dependencies = ActionDependencies(None, ["collect"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_077(self): """ Test with actions=[ store, one ], extensions=[ (one, after collect) ]. """ actions = [ "store", "one", ] dependencies = ActionDependencies([], ["collect"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_078(self): """ Test with actions=[ purge, one ], extensions=[ (one, after collect) ]. """ actions = [ "purge", "one", ] dependencies = ActionDependencies(None, ["collect"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_079(self): """ Test with actions=[ stage, one ], extensions=[ (one, before stage) ]. """ actions = [ "stage", "one", ] dependencies = ActionDependencies(["stage"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_080(self): """ Test with actions=[ store, one ], extensions=[ (one, before stage ) ]. """ actions = [ "store", "one", ] dependencies = ActionDependencies(["stage"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_081(self): """ Test with actions=[ purge, one ], extensions=[ (one, before stage) ]. """ actions = [ "purge", "one", ] dependencies = ActionDependencies(["stage"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_082(self): """ Test with actions=[ all, one ], extensions=[ (one, after collect) ]. """ actions = [ "all", "one", ] dependencies = ActionDependencies(None, ["collect"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_083(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, after collect) ]. """ actions = [ "rebuild", "one", ] dependencies = ActionDependencies([], ["collect"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_084(self): """ Test with actions=[ validate, one ], extensions=[ (one, after collect) ]. """ actions = [ "validate", "one", ] dependencies = ActionDependencies(None, ["collect"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_085(self): """ Test with actions=[ collect, one ], extensions=[ (one, after stage) ]. """ actions = [ "collect", "one", ] dependencies = ActionDependencies([], ["stage"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testDependencyMode_086(self): """ Test with actions=[ stage, one ], extensions=[ (one, after stage) ]. """ actions = [ "stage", "one", ] dependencies = ActionDependencies([], ["stage"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testDependencyMode_087(self): """ Test with actions=[ store, one ], extensions=[ (one, after stage) ]. """ actions = [ "store", "one", ] dependencies = ActionDependencies(None, ["stage"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_088(self): """ Test with actions=[ purge, one ], extensions=[ (one, after stage) ]. """ actions = [ "purge", "one", ] dependencies = ActionDependencies([], ["stage"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_089(self): """ Test with actions=[ collect, one ], extensions=[ (one, before store) ]. """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["store"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_090(self): """ Test with actions=[ stage, one ], extensions=[ (one, before store) ]. """ actions = [ "stage", "one", ] dependencies = ActionDependencies(["store"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_091(self): """ Test with actions=[ store, one ], extensions=[ (one, before store) ]. """ actions = [ "store", "one", ] dependencies = ActionDependencies(["store"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_092(self): """ Test with actions=[ purge, one ], extensions=[ (one, before store) ]. """ actions = [ "purge", "one", ] dependencies = ActionDependencies(["store"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_093(self): """ Test with actions=[ all, one ], extensions=[ (one, after stage) ]. """ actions = [ "all", "one", ] dependencies = ActionDependencies(None, ["stage"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_094(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, after stage) ]. """ actions = [ "rebuild", "one", ] dependencies = ActionDependencies([], ["stage"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_095(self): """ Test with actions=[ validate, one ], extensions=[ (one, after stage) ]. """ actions = [ "validate", "one", ] dependencies = ActionDependencies(None, ["stage"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_096(self): """ Test with actions=[ collect, one ], extensions=[ (one, after store) ]. """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["store"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_097(self): """ Test with actions=[ stage, one ], extensions=[ (one, after store) ]. """ actions = [ "stage", "one", ] dependencies = ActionDependencies(["store"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_098(self): """ Test with actions=[ store, one ], extensions=[ (one, after store) ]. """ actions = [ "store", "one", ] dependencies = ActionDependencies(["store"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testDependencyMode_099(self): """ Test with actions=[ purge, one ], extensions=[ (one, after store) ]. """ actions = [ "purge", "one", ] dependencies = ActionDependencies(["store"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testDependencyMode_100(self): """ Test with actions=[ collect, one ], extensions=[ (one, before purge) ]. """ actions = [ "collect", "one", ] dependencies = ActionDependencies([], ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_101(self): """ Test with actions=[ stage, one ], extensions=[ (one, before purge) ]. """ actions = [ "stage", "one", ] dependencies = ActionDependencies(None, ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) def testDependencyMode_102(self): """ Test with actions=[ store, one ], extensions=[ (one, before purge) ]. """ actions = [ "store", "one", ] dependencies = ActionDependencies([], ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) def testDependencyMode_103(self): """ Test with actions=[ purge, one ], extensions=[ (one, before purge) ]. """ actions = [ "purge", "one", ] dependencies = ActionDependencies(None, ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executePurge, actionSet.actionSet[0].function) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testDependencyMode_104(self): """ Test with actions=[ all, one ], extensions=[ (one, after store) ]. """ actions = [ "all", "one", ] dependencies = ActionDependencies(["store"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_105(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, after store) ]. """ actions = [ "rebuild", "one", ] dependencies = ActionDependencies(["store"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_106(self): """ Test with actions=[ validate, one ], extensions=[ (one, after store) ]. """ actions = [ "validate", "one", ] dependencies = ActionDependencies(["store"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_107(self): """ Test with actions=[ collect, one ], extensions=[ (one, after purge) ]. """ actions = [ "collect", "one", ] dependencies = ActionDependencies(None, ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_108(self): """ Test with actions=[ stage, one ], extensions=[ (one, after purge) ]. """ actions = [ "stage", "one", ] dependencies = ActionDependencies([], ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) def testDependencyMode_109(self): """ Test with actions=[ store, one ], extensions=[ (one, after purge) ]. """ actions = [ "store", "one", ] dependencies = ActionDependencies(None, ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) def testDependencyMode_110(self): """ Test with actions=[ purge, one ], extensions=[ (one, after purge) ]. """ actions = [ "purge", "one", ] dependencies = ActionDependencies([], ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executePurge, actionSet.actionSet[0].function) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testDependencyMode_111(self): """ Test with actions=[ all, one ], extensions=[ (one, after purge) ]. """ actions = [ "all", "one", ] dependencies = ActionDependencies(None, ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_112(self): """ Test with actions=[ rebuild, one ], extensions=[ (one, after purge) ]. """ actions = [ "rebuild", "one", ] dependencies = ActionDependencies([], ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_113(self): """ Test with actions=[ validate, one ], extensions=[ (one, after purge) ]. """ actions = [ "validate", "one", ] dependencies = ActionDependencies(None, ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_114(self): """ Test with actions=[ one, one ], extensions=[ (one, after purge) ]. """ actions = [ "one", "one", ] dependencies = ActionDependencies([], ["purge"]) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(isdir, actionSet.actionSet[1].function) def testDependencyMode_115(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[]. """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testDependencyMode_116(self): """ Test with actions=[ stage, purge, collect, store ], extensions=[]. """ actions = [ "stage", "purge", "collect", "store", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testDependencyMode_117(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ one before collect, two before stage, etc. ]. """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] dependencies1 = ActionDependencies(["collect", "stage", "store", "purge"], None) dependencies2 = ActionDependencies(["stage", "store", "purge"], ["collect"]) dependencies3 = ActionDependencies(["store", "purge"], ["collect", "stage"]) dependencies4 = ActionDependencies(["purge"], ["collect", "stage", "store"]) dependencies5 = ActionDependencies([], ["collect", "stage", "store", "purge"]) eaction1 = ExtendedAction("one", "os.path", "isdir", dependencies=dependencies1) eaction2 = ExtendedAction("two", "os.path", "isfile", dependencies=dependencies2) eaction3 = ExtendedAction("three", "os.path", "islink", dependencies=dependencies3) eaction4 = ExtendedAction("four", "os.path", "isabs", dependencies=dependencies4) eaction5 = ExtendedAction("five", "os.path", "exists", dependencies=dependencies5) extensions = ExtensionsConfig([eaction1, eaction2, eaction3, eaction4, eaction5], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 9) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) self.assertEqual("two", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(isfile, actionSet.actionSet[2].function) self.assertEqual("stage", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executeStage, actionSet.actionSet[3].function) self.assertEqual("three", actionSet.actionSet[4].name) self.assertEqual(None, actionSet.actionSet[4].preHooks) self.assertEqual(None, actionSet.actionSet[4].postHooks) self.assertEqual(islink, actionSet.actionSet[4].function) self.assertEqual("store", actionSet.actionSet[5].name) self.assertEqual(None, actionSet.actionSet[5].preHooks) self.assertEqual(None, actionSet.actionSet[5].postHooks) self.assertEqual(executeStore, actionSet.actionSet[5].function) self.assertEqual("four", actionSet.actionSet[6].name) self.assertEqual(None, actionSet.actionSet[6].preHooks) self.assertEqual(None, actionSet.actionSet[6].postHooks) self.assertEqual(isabs, actionSet.actionSet[6].function) self.assertEqual("purge", actionSet.actionSet[7].name) self.assertEqual(None, actionSet.actionSet[7].preHooks) self.assertEqual(None, actionSet.actionSet[7].postHooks) self.assertEqual(executePurge, actionSet.actionSet[7].function) self.assertEqual("five", actionSet.actionSet[8].name) self.assertEqual(None, actionSet.actionSet[8].preHooks) self.assertEqual(None, actionSet.actionSet[8].postHooks) self.assertEqual(exists, actionSet.actionSet[8].function) def testDependencyMode_118(self): """ Test with actions=[ one, five, collect, store, three, stage, four, purge, two ], extensions=[ one before collect, two before stage, etc. ]. """ actions = [ "one", "five", "collect", "store", "three", "stage", "four", "purge", "two", ] dependencies1 = ActionDependencies(["collect", "stage", "store", "purge"], []) dependencies2 = ActionDependencies(["stage", "store", "purge"], ["collect"]) dependencies3 = ActionDependencies(["store", "purge"], ["collect", "stage"]) dependencies4 = ActionDependencies(["purge"], ["collect", "stage", "store"]) dependencies5 = ActionDependencies(None, ["collect", "stage", "store", "purge"]) eaction1 = ExtendedAction("one", "os.path", "isdir", dependencies=dependencies1) eaction2 = ExtendedAction("two", "os.path", "isfile", dependencies=dependencies2) eaction3 = ExtendedAction("three", "os.path", "islink", dependencies=dependencies3) eaction4 = ExtendedAction("four", "os.path", "isabs", dependencies=dependencies4) eaction5 = ExtendedAction("five", "os.path", "exists", dependencies=dependencies5) extensions = ExtensionsConfig([eaction1, eaction2, eaction3, eaction4, eaction5], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 9) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) self.assertEqual("two", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(isfile, actionSet.actionSet[2].function) self.assertEqual("stage", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executeStage, actionSet.actionSet[3].function) self.assertEqual("three", actionSet.actionSet[4].name) self.assertEqual(None, actionSet.actionSet[4].preHooks) self.assertEqual(None, actionSet.actionSet[4].postHooks) self.assertEqual(islink, actionSet.actionSet[4].function) self.assertEqual("store", actionSet.actionSet[5].name) self.assertEqual(None, actionSet.actionSet[5].preHooks) self.assertEqual(None, actionSet.actionSet[5].postHooks) self.assertEqual(executeStore, actionSet.actionSet[5].function) self.assertEqual("four", actionSet.actionSet[6].name) self.assertEqual(None, actionSet.actionSet[6].preHooks) self.assertEqual(None, actionSet.actionSet[6].postHooks) self.assertEqual(isabs, actionSet.actionSet[6].function) self.assertEqual("purge", actionSet.actionSet[7].name) self.assertEqual(None, actionSet.actionSet[7].preHooks) self.assertEqual(None, actionSet.actionSet[7].postHooks) self.assertEqual(executePurge, actionSet.actionSet[7].function) self.assertEqual("five", actionSet.actionSet[8].name) self.assertEqual(None, actionSet.actionSet[8].preHooks) self.assertEqual(None, actionSet.actionSet[8].postHooks) self.assertEqual(exists, actionSet.actionSet[8].function) def testDependencyMode_119(self): """ Test with actions=[ one ], extensions=[ (one, before collect) ]. """ actions = [ "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testDependencyMode_120(self): """ Test with actions=[ collect ], extensions=[], hooks=[] """ actions = [ "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() options.hooks = [] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_121(self): """ Test with actions=[ collect ], extensions=[], pre-hook on 'stage' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() options.hooks = [PreActionHook("stage", "something")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_122(self): """ Test with actions=[ collect ], extensions=[], post-hook on 'stage' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() options.hooks = [PostActionHook("stage", "something")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_123(self): """ Test with actions=[ collect ], extensions=[], pre-hook on 'collect' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() options.hooks = [PreActionHook("collect", "something")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("collect", "something")], actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_124(self): """ Test with actions=[ collect ], extensions=[], post-hook on 'collect' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() options.hooks = [PostActionHook("collect", "something")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("collect", "something")], actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_125(self): """ Test with actions=[ collect ], extensions=[], pre- and post-hook on 'collect' action. """ actions = [ "collect", ] extensions = ExtensionsConfig([], "dependency") options = OptionsConfig() options.hooks = [PreActionHook("collect", "something1"), PostActionHook("collect", "something2")] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("collect", "something1")], actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("collect", "something2")], actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testDependencyMode_126(self): """ Test with actions=[ one ], extensions=[ (one, before collect) ], hooks=[] """ actions = [ "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testDependencyMode_127(self): """ Test with actions=[ one ], extensions=[ (one, before collect) ], pre-hook on "store" action. """ actions = [ "one", ] dependencies = ActionDependencies(["collect"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PreActionHook("store", "whatever"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testDependencyMode_128(self): """ Test with actions=[ one ], extensions=[ (one, before collect) ], post-hook on "store" action. """ actions = [ "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PostActionHook("store", "whatever"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testDependencyMode_129(self): """ Test with actions=[ one ], extensions=[ (one, before collect) ], pre-hook on "one" action. """ actions = [ "one", ] dependencies = ActionDependencies(["collect"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PreActionHook("one", "extension"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("one", "extension")], actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testDependencyMode_130(self): """ Test with actions=[ one ], extensions=[ (one, before collect) ], post-hook on "one" action. """ actions = [ "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testDependencyMode_131(self): """ Test with actions=[ one ], extensions=[ (one, before collect) ], pre- and post-hook on "one" action. """ actions = [ "one", ] dependencies = ActionDependencies(["collect"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension2"), PreActionHook("one", "extension1"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("one", "extension1")], actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension2")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) def testDependencyMode_132(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ], hooks=[] """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_133(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ], pre-hook on "purge" action """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PreActionHook("purge", "rm -f"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_134(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ], post-hook on "purge" action """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PostActionHook("purge", "rm -f"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_135(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ], pre-hook on "collect" action """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PreActionHook("collect", "something"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual([PreActionHook("collect", "something")], actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_136(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ], post-hook on "collect" action """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PostActionHook("collect", "something"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual([PostActionHook("collect", "something")], actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_137(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ], pre-hook on "one" action """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PreActionHook("one", "extension"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual([PreActionHook("one", "extension")], actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_138(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ], post-hook on "one" action """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], []) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_139a(self): """ Test with actions=[ collect, one ], extensions=[ (one, before collect) ], set of various pre- and post hooks. """ actions = [ "collect", "one", ] dependencies = ActionDependencies(["collect"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension"), PreActionHook("collect", "something1"), PreActionHook("collect", "something2"), PostActionHook("stage", "whatever"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual( [PreActionHook("collect", "something1"), PreActionHook("collect", "something2")], actionSet.actionSet[1].preHooks ) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testDependencyMode_139b(self): """ Test with actions=[ stage, one ], extensions=[ (one, before stage) ], set of various pre- and post hooks. """ actions = [ "stage", "one", ] dependencies = ActionDependencies(["stage"], None) extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", dependencies=dependencies)], "dependency") options = OptionsConfig() options.hooks = [ PostActionHook("one", "extension"), PreActionHook("collect", "something1"), PostActionHook("stage", "whatever1"), PostActionHook("stage", "whatever2"), ] actionSet = _ActionSet(actions, extensions, options, None, False, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual([PostActionHook("one", "extension")], actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual( [PostActionHook("stage", "whatever1"), PostActionHook("stage", "whatever2")], actionSet.actionSet[1].postHooks ) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testDependencyMode_140(self): """ Test with actions=[ one, five, collect, store, three, stage, four, purge, two ], extensions= [recursive loop]. """ actions = [ "one", "five", "collect", "store", "three", "stage", "four", "purge", "two", ] dependencies1 = ActionDependencies(["collect", "stage", "store", "purge"], []) dependencies2 = ActionDependencies(["stage", "store", "purge"], ["collect"]) dependencies3 = ActionDependencies(["store", "purge"], ["collect", "stage"]) dependencies4 = ActionDependencies(["purge"], ["collect", "stage", "store"]) dependencies5 = ActionDependencies(["one"], ["collect", "stage", "store", "purge"]) eaction1 = ExtendedAction("one", "os.path", "isdir", dependencies=dependencies1) eaction2 = ExtendedAction("two", "os.path", "isfile", dependencies=dependencies2) eaction3 = ExtendedAction("three", "os.path", "islink", dependencies=dependencies3) eaction4 = ExtendedAction("four", "os.path", "isabs", dependencies=dependencies4) eaction5 = ExtendedAction("five", "os.path", "exists", dependencies=dependencies5) extensions = ExtensionsConfig([eaction1, eaction2, eaction3, eaction4, eaction5], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) def testDependencyMode_141(self): """ Test with actions=[ one, five, collect, store, three, stage, four, purge, two ], and one extension for which a dependency does not exist. """ actions = [ "one", "five", "collect", "store", "three", "stage", "four", "purge", "two", ] dependencies1 = ActionDependencies(["collect", "stage", "store", "purge"], []) dependencies2 = ActionDependencies(["stage", "store", "purge"], ["collect"]) dependencies3 = ActionDependencies(["store", "bogus"], ["collect", "stage"]) dependencies4 = ActionDependencies(["purge"], ["collect", "stage", "store"]) dependencies5 = ActionDependencies([], ["collect", "stage", "store", "purge"]) eaction1 = ExtendedAction("one", "os.path", "isdir", dependencies=dependencies1) eaction2 = ExtendedAction("two", "os.path", "isfile", dependencies=dependencies2) eaction3 = ExtendedAction("three", "os.path", "islink", dependencies=dependencies3) eaction4 = ExtendedAction("four", "os.path", "isabs", dependencies=dependencies4) eaction5 = ExtendedAction("five", "os.path", "exists", dependencies=dependencies5) extensions = ExtensionsConfig([eaction1, eaction2, eaction3, eaction4, eaction5], "dependency") options = OptionsConfig() self.assertRaises(ValueError, _ActionSet, actions, extensions, options, None, False, True) ######################################### # Test constructor, with managed peers ######################################### def testManagedPeer_001(self): """ Test with actions=[ collect ], extensions=[], peers=None, managed=True, local=True """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testManagedPeer_002(self): """ Test with actions=[ stage ], extensions=[], peers=None, managed=True, local=True """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(executeStage, actionSet.actionSet[0].function) def testManagedPeer_003(self): """ Test with actions=[ store ], extensions=[], peers=None, managed=True, local=True """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(executeStore, actionSet.actionSet[0].function) def testManagedPeer_004(self): """ Test with actions=[ purge ], extensions=[], peers=None, managed=True, local=True """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(executePurge, actionSet.actionSet[0].function) def testManagedPeer_005(self): """ Test with actions=[ all ], extensions=[], peers=None, managed=True, local=True """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testManagedPeer_006(self): """ Test with actions=[ rebuild ], extensions=[], peers=None, managed=True, local=True """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("rebuild", actionSet.actionSet[0].name) self.assertEqual(executeRebuild, actionSet.actionSet[0].function) def testManagedPeer_007(self): """ Test with actions=[ validate ], extensions=[], peers=None, managed=True, local=True """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("validate", actionSet.actionSet[0].name) self.assertEqual(executeValidate, actionSet.actionSet[0].function) def testManagedPeer_008(self): """ Test with actions=[ collect, stage ], extensions=[], peers=None, managed=True, local=True """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testManagedPeer_009(self): """ Test with actions=[ collect, store ], extensions=[], peers=None, managed=True, local=True """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_010(self): """ Test with actions=[ collect, purge ], extensions=[], peers=None, managed=True, local=True """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testManagedPeer_011(self): """ Test with actions=[ stage, collect ], extensions=[], peers=None, managed=True, local=True """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testManagedPeer_012(self): """ Test with actions=[ stage, stage ], extensions=[], peers=None, managed=True, local=True """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testManagedPeer_013(self): """ Test with actions=[ stage, store ], extensions=[], peers=None, managed=True, local=True """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_014(self): """ Test with actions=[ stage, purge ], extensions=[], peers=None, managed=True, local=True """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testManagedPeer_015(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], peers=None, managed=True, local=True """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testManagedPeer_016(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ], peers=None, managed=True, local=True """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_017(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ], peers=None, managed=True, local=True """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(150, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(isdir, actionSet.actionSet[1].function) def testManagedPeer_018(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ], peers=None, managed=True, local=True """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_019(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[], peers=None, managed=True, local=True """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testManagedPeer_020(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)], peers=None, managed=True, local=True """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 9) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(executeCollect, actionSet.actionSet[1].function) self.assertEqual(150, actionSet.actionSet[2].index) self.assertEqual("two", actionSet.actionSet[2].name) self.assertEqual(isfile, actionSet.actionSet[2].function) self.assertEqual(200, actionSet.actionSet[3].index) self.assertEqual("stage", actionSet.actionSet[3].name) self.assertEqual(executeStage, actionSet.actionSet[3].function) self.assertEqual(250, actionSet.actionSet[4].index) self.assertEqual("three", actionSet.actionSet[4].name) self.assertEqual(islink, actionSet.actionSet[4].function) self.assertEqual(300, actionSet.actionSet[5].index) self.assertEqual("store", actionSet.actionSet[5].name) self.assertEqual(executeStore, actionSet.actionSet[5].function) self.assertEqual(350, actionSet.actionSet[6].index) self.assertEqual("four", actionSet.actionSet[6].name) self.assertEqual(isabs, actionSet.actionSet[6].function) self.assertEqual(400, actionSet.actionSet[7].index) self.assertEqual("purge", actionSet.actionSet[7].name) self.assertEqual(executePurge, actionSet.actionSet[7].function) self.assertEqual(450, actionSet.actionSet[8].index) self.assertEqual("five", actionSet.actionSet[8].name) self.assertEqual(exists, actionSet.actionSet[8].function) def testManagedPeer_021(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], peers=None, managed=True, local=True """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] actionSet = _ActionSet(actions, extensions, options, None, True, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) def testManagedPeer_022(self): """ Test with actions=[ collect ], extensions=[], no peers, managed=True, local=True """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) def testManagedPeer_023(self): """ Test with actions=[ stage ], extensions=[], no peers, managed=True, local=True """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(executeStage, actionSet.actionSet[0].function) def testManagedPeer_024(self): """ Test with actions=[ store ], extensions=[], no peers, managed=True, local=True """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(executeStore, actionSet.actionSet[0].function) def testManagedPeer_025(self): """ Test with actions=[ purge ], extensions=[], no peers, managed=True, local=True """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(executePurge, actionSet.actionSet[0].function) def testManagedPeer_026(self): """ Test with actions=[ all ], extensions=[], no peers, managed=True, local=True """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testManagedPeer_027(self): """ Test with actions=[ rebuild ], extensions=[], no peers, managed=True, local=True """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("rebuild", actionSet.actionSet[0].name) self.assertEqual(executeRebuild, actionSet.actionSet[0].function) def testManagedPeer_028(self): """ Test with actions=[ validate ], extensions=[], no peers, managed=True, local=True """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("validate", actionSet.actionSet[0].name) self.assertEqual(executeValidate, actionSet.actionSet[0].function) def testManagedPeer_029(self): """ Test with actions=[ collect, stage ], extensions=[], no peers, managed=True, local=True """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testManagedPeer_030(self): """ Test with actions=[ collect, store ], extensions=[], no peers, managed=True, local=True """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_031(self): """ Test with actions=[ collect, purge ], extensions=[], no peers, managed=True, local=True """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testManagedPeer_032(self): """ Test with actions=[ stage, collect ], extensions=[], no peers, managed=True, local=True """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testManagedPeer_033(self): """ Test with actions=[ stage, stage ], extensions=[], no peers, managed=True, local=True """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testManagedPeer_034(self): """ Test with actions=[ stage, store ], extensions=[], no peers, managed=True, local=True """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_035(self): """ Test with actions=[ stage, purge ], extensions=[], no peers, managed=True, local=True """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(executePurge, actionSet.actionSet[1].function) def testManagedPeer_036(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], no peers, managed=True, local=True """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(executeCollect, actionSet.actionSet[1].function) def testManagedPeer_037(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ], no peers, managed=True, local=True """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_038(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ], no peers, managed=True, local=True """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(150, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertEqual(isdir, actionSet.actionSet[1].function) def testManagedPeer_039(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ], no peers, managed=True, local=True """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_040(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[], no peers, managed=True, local=True """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(executeStage, actionSet.actionSet[1].function) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(executeStore, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertEqual(executePurge, actionSet.actionSet[3].function) def testManagedPeer_041(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)], no peers, managed=True, local=True """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 9) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertEqual(executeCollect, actionSet.actionSet[1].function) self.assertEqual(150, actionSet.actionSet[2].index) self.assertEqual("two", actionSet.actionSet[2].name) self.assertEqual(isfile, actionSet.actionSet[2].function) self.assertEqual(200, actionSet.actionSet[3].index) self.assertEqual("stage", actionSet.actionSet[3].name) self.assertEqual(executeStage, actionSet.actionSet[3].function) self.assertEqual(250, actionSet.actionSet[4].index) self.assertEqual("three", actionSet.actionSet[4].name) self.assertEqual(islink, actionSet.actionSet[4].function) self.assertEqual(300, actionSet.actionSet[5].index) self.assertEqual("store", actionSet.actionSet[5].name) self.assertEqual(executeStore, actionSet.actionSet[5].function) self.assertEqual(350, actionSet.actionSet[6].index) self.assertEqual("four", actionSet.actionSet[6].name) self.assertEqual(isabs, actionSet.actionSet[6].function) self.assertEqual(400, actionSet.actionSet[7].index) self.assertEqual("purge", actionSet.actionSet[7].name) self.assertEqual(executePurge, actionSet.actionSet[7].function) self.assertEqual(450, actionSet.actionSet[8].index) self.assertEqual("five", actionSet.actionSet[8].name) self.assertEqual(exists, actionSet.actionSet[8].function) def testManagedPeer_042(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], no peers, managed=True, local=True """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(isdir, actionSet.actionSet[0].function) def testManagedPeer_043(self): """ Test with actions=[ collect ], extensions=[], no peers, managed=True, local=False """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_044(self): """ Test with actions=[ stage ], extensions=[], no peers, managed=True, local=False """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_045(self): """ Test with actions=[ store ], extensions=[], no peers, managed=True, local=False """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_046(self): """ Test with actions=[ purge ], extensions=[], no peers, managed=True, local=False """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_047(self): """ Test with actions=[ all ], extensions=[], no peers, managed=True, local=False """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_048(self): """ Test with actions=[ rebuild ], extensions=[], no peers, managed=True, local=False """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_049(self): """ Test with actions=[ validate ], extensions=[], no peers, managed=True, local=False """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_050(self): """ Test with actions=[ collect, stage ], extensions=[], no peers, managed=True, local=False """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_051(self): """ Test with actions=[ collect, store ], extensions=[], no peers, managed=True, local=False """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_052(self): """ Test with actions=[ collect, purge ], extensions=[], no peers, managed=True, local=False """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_053(self): """ Test with actions=[ stage, collect ], extensions=[], no peers, managed=True, local=False """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_054(self): """ Test with actions=[ stage, stage ], extensions=[], no peers, managed=True, local=False """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_055(self): """ Test with actions=[ stage, store ], extensions=[], no peers, managed=True, local=False """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_056(self): """ Test with actions=[ stage, purge ], extensions=[], no peers, managed=True, local=False """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_057(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], no peers, managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_058(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ], no peers, managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_059(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ], no peers, managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_060(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ], no peers, managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_061(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[], no peers, managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_062(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)], no peers, managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_063(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], no peers, managed=True, local=False """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_064(self): """ Test with actions=[ collect ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_065(self): """ Test with actions=[ stage ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_066(self): """ Test with actions=[ store ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_067(self): """ Test with actions=[ purge ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_068(self): """ Test with actions=[ all ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_069(self): """ Test with actions=[ rebuild ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_070(self): """ Test with actions=[ validate ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_071(self): """ Test with actions=[ collect, stage ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_072(self): """ Test with actions=[ collect, store ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_073(self): """ Test with actions=[ collect, purge ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_074(self): """ Test with actions=[ stage, collect ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_075(self): """ Test with actions=[ stage, stage ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_076(self): """ Test with actions=[ stage, store ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_077(self): """ Test with actions=[ stage, purge ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_078(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], one peer (not managed), managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_079(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ], one peer (not managed), managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_080(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ], one peer (not managed), managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_081(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ], one peer (not managed), managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_082(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[], one peer (not managed), managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_083(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)], one peer (not managed), managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_084(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], one peer (not managed), managed=True, local=False """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_085(self): """ Test with actions=[ collect ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", None, "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_086(self): """ Test with actions=[ stage ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_087(self): """ Test with actions=[ store ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_088(self): """ Test with actions=[ purge ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_089(self): """ Test with actions=[ all ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) def testManagedPeer_090(self): """ Test with actions=[ rebuild ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_091(self): """ Test with actions=[ validate ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_092(self): """ Test with actions=[ collect, stage ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_093(self): """ Test with actions=[ collect, store ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_094(self): """ Test with actions=[ collect, purge ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) def testManagedPeer_095(self): """ Test with actions=[ stage, collect ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_096(self): """ Test with actions=[ stage, stage ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_097(self): """ Test with actions=[ stage, store ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_098(self): """ Test with actions=[ stage, purge ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_099(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], one peer (managed), managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_100(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ], one peer (managed), managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_101(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ], one peer (managed), managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(150, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) def testManagedPeer_102(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ], one peer (managed), managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_103(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[], one peer (managed), managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) def testManagedPeer_104(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)], one peer (managed), managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual(400, actionSet.actionSet[2].index) self.assertEqual("purge", actionSet.actionSet[2].name) self.assertFalse(actionSet.actionSet[2].remotePeers is None) self.assertTrue(len(actionSet.actionSet[2].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[2].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[2].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[2].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[2].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[2].remotePeers[0].cbackCommand) def testManagedPeer_105(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], one peer (managed), managed=True, local=False """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_106(self): """ Test with actions=[ collect ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", None, "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_107(self): """ Test with actions=[ stage ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_108(self): """ Test with actions=[ store ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_109(self): """ Test with actions=[ purge ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_110(self): """ Test with actions=[ all ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) def testManagedPeer_111(self): """ Test with actions=[ rebuild ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_112(self): """ Test with actions=[ validate ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_113(self): """ Test with actions=[ collect, stage ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_114(self): """ Test with actions=[ collect, store ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_115(self): """ Test with actions=[ collect, purge ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) def testManagedPeer_116(self): """ Test with actions=[ stage, collect ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_117(self): """ Test with actions=[ stage, stage ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_118(self): """ Test with actions=[ stage, store ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_119(self): """ Test with actions=[ stage, purge ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_120(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], two peers (one managed, one not), managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_121(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ], two peers (one managed, one not), managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_122(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ], two peers (one managed, one not), managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(150, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) def testManagedPeer_123(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ], two peers (one managed, one not), managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_124(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[], two peers (one managed, one not), managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) def testManagedPeer_125(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)], two peers (one managed, one not), managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual(400, actionSet.actionSet[2].index) self.assertEqual("purge", actionSet.actionSet[2].name) self.assertFalse(actionSet.actionSet[2].remotePeers is None) self.assertTrue(len(actionSet.actionSet[2].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[2].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[2].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[2].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[2].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[2].remotePeers[0].cbackCommand) def testManagedPeer_126(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], two peers (one managed, one not), managed=True, local=False """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=False), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) def testManagedPeer_127(self): """ Test with actions=[ collect ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", None, "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_128(self): """ Test with actions=[ stage ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_129(self): """ Test with actions=[ store ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_130(self): """ Test with actions=[ purge ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_131(self): """ Test with actions=[ all ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) def testManagedPeer_132(self): """ Test with actions=[ rebuild ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_133(self): """ Test with actions=[ validate ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_134(self): """ Test with actions=[ collect, stage ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_135(self): """ Test with actions=[ collect, store ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_136(self): """ Test with actions=[ collect, purge ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) def testManagedPeer_137(self): """ Test with actions=[ stage, collect ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_138(self): """ Test with actions=[ stage, stage ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_139(self): """ Test with actions=[ stage, store ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 0) def testManagedPeer_140(self): """ Test with actions=[ stage, purge ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_141(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], two peers (both managed), managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) def testManagedPeer_142(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ], two peers (both managed), managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_143(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ], two peers (both managed), managed=True, local=False """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) self.assertEqual(150, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) def testManagedPeer_144(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ], two peers (both managed), managed=True, local=False """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_145(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[], two peers (both managed), managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) def testManagedPeer_146(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)], two peers (both managed), managed=True, local=False """ actions = [ "collect", "stage", "store", "purge", "one", "two", "three", "four", "five", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(400, actionSet.actionSet[2].index) self.assertEqual("purge", actionSet.actionSet[2].name) self.assertFalse(actionSet.actionSet[2].remotePeers is None) self.assertTrue(len(actionSet.actionSet[2].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[2].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[2].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[2].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[2].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[2].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[2].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[2].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[2].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[2].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[2].remotePeers[1].cbackCommand) def testManagedPeer_147(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], two peers (both managed), managed=True, local=False """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, False) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertFalse(actionSet.actionSet[0].remotePeers is None) self.assertTrue(len(actionSet.actionSet[0].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[0].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[0].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[0].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[0].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[0].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[0].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[0].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[0].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[0].remotePeers[1].cbackCommand) def testManagedPeer_148(self): """ Test with actions=[ collect ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", None, "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) def testManagedPeer_149(self): """ Test with actions=[ stage ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) def testManagedPeer_150(self): """ Test with actions=[ store ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(300, actionSet.actionSet[0].index) self.assertEqual("store", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStore, actionSet.actionSet[0].function) def testManagedPeer_151(self): """ Test with actions=[ purge ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(400, actionSet.actionSet[0].index) self.assertEqual("purge", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executePurge, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) def testManagedPeer_152(self): """ Test with actions=[ all ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "all", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertFalse(actionSet.actionSet is None) self.assertTrue(len(actionSet.actionSet) == 6) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(200, actionSet.actionSet[2].index) self.assertEqual("stage", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStage, actionSet.actionSet[2].function) self.assertEqual(300, actionSet.actionSet[3].index) self.assertEqual("store", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executeStore, actionSet.actionSet[3].function) self.assertEqual(400, actionSet.actionSet[4].index) self.assertEqual("purge", actionSet.actionSet[4].name) self.assertEqual(None, actionSet.actionSet[4].preHooks) self.assertEqual(None, actionSet.actionSet[4].postHooks) self.assertEqual(executePurge, actionSet.actionSet[4].function) self.assertEqual(400, actionSet.actionSet[5].index) self.assertEqual("purge", actionSet.actionSet[5].name) self.assertFalse(actionSet.actionSet[5].remotePeers is None) self.assertTrue(len(actionSet.actionSet[5].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[5].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[5].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[5].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[5].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[5].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[5].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[5].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[5].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[5].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[5].remotePeers[1].cbackCommand) def testManagedPeer_153(self): """ Test with actions=[ rebuild ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "rebuild", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("rebuild", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeRebuild, actionSet.actionSet[0].function) def testManagedPeer_154(self): """ Test with actions=[ validate ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "validate", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 1) self.assertEqual(0, actionSet.actionSet[0].index) self.assertEqual("validate", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeValidate, actionSet.actionSet[0].function) def testManagedPeer_155(self): """ Test with actions=[ collect, stage ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "collect", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(200, actionSet.actionSet[2].index) self.assertEqual("stage", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStage, actionSet.actionSet[2].function) def testManagedPeer_156(self): """ Test with actions=[ collect, store ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "collect", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) def testManagedPeer_157(self): """ Test with actions=[ collect, purge ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "collect", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(400, actionSet.actionSet[2].index) self.assertEqual("purge", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executePurge, actionSet.actionSet[2].function) self.assertEqual(400, actionSet.actionSet[3].index) self.assertEqual("purge", actionSet.actionSet[3].name) self.assertFalse(actionSet.actionSet[3].remotePeers is None) self.assertTrue(len(actionSet.actionSet[3].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[3].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[3].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[3].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[3].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[3].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[3].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[3].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[3].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[3].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[3].remotePeers[1].cbackCommand) def testManagedPeer_158(self): """ Test with actions=[ stage, collect ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "stage", "collect", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(200, actionSet.actionSet[2].index) self.assertEqual("stage", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStage, actionSet.actionSet[2].function) def testManagedPeer_159(self): """ Test with actions=[ stage, stage ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "stage", "stage", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(200, actionSet.actionSet[1].index) self.assertEqual("stage", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStage, actionSet.actionSet[1].function) def testManagedPeer_160(self): """ Test with actions=[ stage, store ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "stage", "store", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(300, actionSet.actionSet[1].index) self.assertEqual("store", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executeStore, actionSet.actionSet[1].function) def testManagedPeer_161(self): """ Test with actions=[ stage, purge ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "stage", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(200, actionSet.actionSet[0].index) self.assertEqual("stage", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeStage, actionSet.actionSet[0].function) self.assertEqual(400, actionSet.actionSet[1].index) self.assertEqual("purge", actionSet.actionSet[1].name) self.assertEqual(None, actionSet.actionSet[1].preHooks) self.assertEqual(None, actionSet.actionSet[1].postHooks) self.assertEqual(executePurge, actionSet.actionSet[1].function) self.assertEqual(400, actionSet.actionSet[2].index) self.assertEqual("purge", actionSet.actionSet[2].name) self.assertFalse(actionSet.actionSet[2].remotePeers is None) self.assertTrue(len(actionSet.actionSet[2].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[2].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[2].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[2].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[2].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[2].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[2].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[2].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[2].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[2].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[2].remotePeers[1].cbackCommand) def testManagedPeer_162(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 50) ], two peers (both managed), managed=True, local=True """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(50, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(100, actionSet.actionSet[2].index) self.assertEqual("collect", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[2].function) self.assertEqual(100, actionSet.actionSet[3].index) self.assertEqual("collect", actionSet.actionSet[3].name) self.assertFalse(actionSet.actionSet[3].remotePeers is None) self.assertTrue(len(actionSet.actionSet[3].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[3].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[3].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[3].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[3].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[3].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[3].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[3].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[3].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[3].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[3].remotePeers[1].cbackCommand) def testManagedPeer_163(self): """ Test with actions=[ store, one ], extensions=[ (one, index 50) ], two peers (both managed), managed=True, local=True """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(50, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) def testManagedPeer_164(self): """ Test with actions=[ collect, one ], extensions=[ (one, index 150) ], two peers (both managed), managed=True, local=True """ actions = [ "collect", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 4) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(150, actionSet.actionSet[2].index) self.assertEqual("one", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(isdir, actionSet.actionSet[2].function) self.assertEqual(150, actionSet.actionSet[3].index) self.assertEqual("one", actionSet.actionSet[3].name) self.assertFalse(actionSet.actionSet[3].remotePeers is None) self.assertTrue(len(actionSet.actionSet[3].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[3].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[3].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[3].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[3].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[3].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[3].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[3].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[3].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[3].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[3].remotePeers[1].cbackCommand) def testManagedPeer_165(self): """ Test with actions=[ store, one ], extensions=[ (one, index 150) ], two peers (both managed), managed=True, local=True """ actions = [ "store", "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 150)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 3) self.assertEqual(150, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(150, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(300, actionSet.actionSet[2].index) self.assertEqual("store", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStore, actionSet.actionSet[2].function) def testManagedPeer_166(self): """ Test with actions=[ collect, stage, store, purge ], extensions=[], two peers (both managed), managed=True, local=True """ actions = [ "collect", "stage", "store", "purge", ] extensions = ExtensionsConfig([], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 6) self.assertEqual(100, actionSet.actionSet[0].index) self.assertEqual("collect", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[0].function) self.assertEqual(100, actionSet.actionSet[1].index) self.assertEqual("collect", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(200, actionSet.actionSet[2].index) self.assertEqual("stage", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeStage, actionSet.actionSet[2].function) self.assertEqual(300, actionSet.actionSet[3].index) self.assertEqual("store", actionSet.actionSet[3].name) self.assertEqual(None, actionSet.actionSet[3].preHooks) self.assertEqual(None, actionSet.actionSet[3].postHooks) self.assertEqual(executeStore, actionSet.actionSet[3].function) self.assertEqual(400, actionSet.actionSet[4].index) self.assertEqual("purge", actionSet.actionSet[4].name) self.assertEqual(None, actionSet.actionSet[4].preHooks) self.assertEqual(None, actionSet.actionSet[4].postHooks) self.assertEqual(executePurge, actionSet.actionSet[4].function) self.assertEqual(400, actionSet.actionSet[5].index) self.assertEqual("purge", actionSet.actionSet[5].name) self.assertFalse(actionSet.actionSet[5].remotePeers is None) self.assertTrue(len(actionSet.actionSet[5].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[5].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[5].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[5].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[5].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[5].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[5].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[5].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[5].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[5].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[5].remotePeers[1].cbackCommand) def testManagedPeer_167(self): """ Test with actions=[ collect, stage, store, purge, one, two, three, four, five ], extensions=[ (index 50, 150, 250, 350, 450)], two peers (both managed), managed=True, local=True """ actions = [ "collect", "stage", "store", "purge", "one", "two", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 9) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) self.assertEqual(100, actionSet.actionSet[2].index) self.assertEqual("collect", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[2].function) self.assertEqual(100, actionSet.actionSet[3].index) self.assertEqual("collect", actionSet.actionSet[3].name) self.assertFalse(actionSet.actionSet[3].remotePeers is None) self.assertTrue(len(actionSet.actionSet[3].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[3].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[3].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[3].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[3].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[3].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[3].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[3].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[3].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[3].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[3].remotePeers[1].cbackCommand) self.assertEqual(150, actionSet.actionSet[4].index) self.assertEqual("two", actionSet.actionSet[4].name) self.assertEqual(None, actionSet.actionSet[4].preHooks) self.assertEqual(None, actionSet.actionSet[4].postHooks) self.assertEqual(isfile, actionSet.actionSet[4].function) self.assertEqual(200, actionSet.actionSet[5].index) self.assertEqual("stage", actionSet.actionSet[5].name) self.assertEqual(None, actionSet.actionSet[5].preHooks) self.assertEqual(None, actionSet.actionSet[5].postHooks) self.assertEqual(executeStage, actionSet.actionSet[5].function) self.assertEqual(300, actionSet.actionSet[6].index) self.assertEqual("store", actionSet.actionSet[6].name) self.assertEqual(None, actionSet.actionSet[6].preHooks) self.assertEqual(None, actionSet.actionSet[6].postHooks) self.assertEqual(executeStore, actionSet.actionSet[6].function) self.assertEqual(400, actionSet.actionSet[7].index) self.assertEqual("purge", actionSet.actionSet[7].name) self.assertEqual(None, actionSet.actionSet[7].preHooks) self.assertEqual(None, actionSet.actionSet[7].postHooks) self.assertEqual(executePurge, actionSet.actionSet[7].function) self.assertEqual(400, actionSet.actionSet[8].index) self.assertEqual("purge", actionSet.actionSet[8].name) self.assertFalse(actionSet.actionSet[8].remotePeers is None) self.assertTrue(len(actionSet.actionSet[8].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[8].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[8].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[8].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[8].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[8].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[8].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[8].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[8].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[8].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[8].remotePeers[1].cbackCommand) def testManagedPeer_168(self): """ Test with actions=[ one ], extensions=[ (one, index 50) ], two peers (both managed), managed=True, local=True """ actions = [ "one", ] extensions = ExtensionsConfig([ExtendedAction("one", "os.path", "isdir", 50)], None) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, "ruser", "rcp", "rsh", "cback", managed=True), RemotePeer("remote2", None, "ruser2", "rcp2", "rsh2", "cback2", managed=True), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 2) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(50, actionSet.actionSet[1].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 2) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("ruser", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rsh", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual("remote2", actionSet.actionSet[1].remotePeers[1].name) self.assertEqual("ruser2", actionSet.actionSet[1].remotePeers[1].remoteUser) self.assertEqual(None, actionSet.actionSet[1].remotePeers[1].localUser) self.assertEqual("rsh2", actionSet.actionSet[1].remotePeers[1].rshCommand) self.assertEqual("cback2", actionSet.actionSet[1].remotePeers[1].cbackCommand) def testManagedPeer_169(self): """ Test to make sure that various options all seem to be pulled from the right places with mixed data. """ actions = [ "collect", "stage", "store", "purge", "one", "two", ] extensions = ExtensionsConfig( [ ExtendedAction("one", "os.path", "isdir", 50), ExtendedAction("two", "os.path", "isfile", 150), ExtendedAction("three", "os.path", "islink", 250), ExtendedAction("four", "os.path", "isabs", 350), ExtendedAction("five", "os.path", "exists", 450), ], None, ) options = OptionsConfig() options.managedActions = [ "collect", "purge", "one", ] options.backupUser = "userZ" options.rshCommand = "rshZ" options.cbackCommand = "cbackZ" peers = PeersConfig() peers.localPeers = [ LocalPeer("local", "/collect"), ] peers.remotePeers = [ RemotePeer("remote", None, None, None, None, "cback", managed=True), RemotePeer("remote2", None, "ruser2", None, "rsh2", None, managed=True, managedActions=["stage"]), ] actionSet = _ActionSet(actions, extensions, options, peers, True, True) self.assertTrue(len(actionSet.actionSet) == 10) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[0].name) self.assertEqual(None, actionSet.actionSet[0].preHooks) self.assertEqual(None, actionSet.actionSet[0].postHooks) self.assertEqual(isdir, actionSet.actionSet[0].function) self.assertEqual(50, actionSet.actionSet[0].index) self.assertEqual("one", actionSet.actionSet[1].name) self.assertFalse(actionSet.actionSet[1].remotePeers is None) self.assertTrue(len(actionSet.actionSet[1].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[1].remotePeers[0].name) self.assertEqual("userZ", actionSet.actionSet[1].remotePeers[0].remoteUser) self.assertEqual("userZ", actionSet.actionSet[1].remotePeers[0].localUser) self.assertEqual("rshZ", actionSet.actionSet[1].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[1].remotePeers[0].cbackCommand) self.assertEqual(100, actionSet.actionSet[2].index) self.assertEqual("collect", actionSet.actionSet[2].name) self.assertEqual(None, actionSet.actionSet[2].preHooks) self.assertEqual(None, actionSet.actionSet[2].postHooks) self.assertEqual(executeCollect, actionSet.actionSet[2].function) self.assertEqual(100, actionSet.actionSet[3].index) self.assertEqual("collect", actionSet.actionSet[3].name) self.assertFalse(actionSet.actionSet[3].remotePeers is None) self.assertTrue(len(actionSet.actionSet[3].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[3].remotePeers[0].name) self.assertEqual("userZ", actionSet.actionSet[3].remotePeers[0].remoteUser) self.assertEqual("userZ", actionSet.actionSet[3].remotePeers[0].localUser) self.assertEqual("rshZ", actionSet.actionSet[3].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[3].remotePeers[0].cbackCommand) self.assertEqual(150, actionSet.actionSet[4].index) self.assertEqual("two", actionSet.actionSet[4].name) self.assertEqual(None, actionSet.actionSet[4].preHooks) self.assertEqual(None, actionSet.actionSet[4].postHooks) self.assertEqual(isfile, actionSet.actionSet[4].function) self.assertEqual(200, actionSet.actionSet[5].index) self.assertEqual("stage", actionSet.actionSet[5].name) self.assertEqual(None, actionSet.actionSet[5].preHooks) self.assertEqual(None, actionSet.actionSet[5].postHooks) self.assertEqual(executeStage, actionSet.actionSet[5].function) self.assertEqual(200, actionSet.actionSet[6].index) self.assertEqual("stage", actionSet.actionSet[6].name) self.assertFalse(actionSet.actionSet[6].remotePeers is None) self.assertTrue(len(actionSet.actionSet[6].remotePeers) == 1) self.assertEqual("remote2", actionSet.actionSet[6].remotePeers[0].name) self.assertEqual("ruser2", actionSet.actionSet[6].remotePeers[0].remoteUser) self.assertEqual("userZ", actionSet.actionSet[6].remotePeers[0].localUser) self.assertEqual("rsh2", actionSet.actionSet[6].remotePeers[0].rshCommand) self.assertEqual("cbackZ", actionSet.actionSet[6].remotePeers[0].cbackCommand) self.assertEqual(300, actionSet.actionSet[7].index) self.assertEqual("store", actionSet.actionSet[7].name) self.assertEqual(None, actionSet.actionSet[7].preHooks) self.assertEqual(None, actionSet.actionSet[7].postHooks) self.assertEqual(executeStore, actionSet.actionSet[7].function) self.assertEqual(400, actionSet.actionSet[8].index) self.assertEqual("purge", actionSet.actionSet[8].name) self.assertEqual(None, actionSet.actionSet[8].preHooks) self.assertEqual(None, actionSet.actionSet[8].postHooks) self.assertEqual(executePurge, actionSet.actionSet[8].function) self.assertEqual(400, actionSet.actionSet[9].index) self.assertEqual("purge", actionSet.actionSet[9].name) self.assertFalse(actionSet.actionSet[9].remotePeers is None) self.assertTrue(len(actionSet.actionSet[9].remotePeers) == 1) self.assertEqual("remote", actionSet.actionSet[9].remotePeers[0].name) self.assertEqual("userZ", actionSet.actionSet[9].remotePeers[0].remoteUser) self.assertEqual("userZ", actionSet.actionSet[9].remotePeers[0].localUser) self.assertEqual("rshZ", actionSet.actionSet[9].remotePeers[0].rshCommand) self.assertEqual("cback", actionSet.actionSet[9].remotePeers[0].cbackCommand) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1569562 cedar_backup3-3.8.1/tests/test_config.py0000644000000000000000000204300314567004737015176 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2011,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests configuration functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/config.py. Code Coverage ============= This module contains individual tests for the public functions and classes implemented in config.py. I usually prefer to test only the public interface to a class, because that way the regression tests don't depend on the internal implementation. In this case, I've decided to test some of the private methods, because their "privateness" is more a matter of presenting a clean external interface than anything else. In particular, this is the case with the private validation functions (I use the private functions so I can test just the validations for one specific case, even if the public interface only exposes one broad validation). Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract the XML and then feed it back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a CONFIGTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.config import ( ActionDependencies, ActionHook, BlankBehavior, ByteQuantity, CollectConfig, CollectDir, CollectFile, CommandOverride, Config, ExtendedAction, ExtensionsConfig, LocalPeer, OptionsConfig, PeersConfig, PostActionHook, PreActionHook, PurgeConfig, PurgeDir, ReferenceConfig, RemotePeer, StageConfig, StoreConfig, ) from CedarBackup3.testutil import configureLogging, failUnlessAssignRaises, findResources from CedarBackup3.util import UNIT_BYTES, UNIT_GBYTES, UNIT_KBYTES, UNIT_MBYTES ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "cback.conf.1", "cback.conf.2", "cback.conf.3", "cback.conf.4", "cback.conf.5", "cback.conf.6", "cback.conf.7", "cback.conf.8", "cback.conf.9", "cback.conf.10", "cback.conf.11", "cback.conf.12", "cback.conf.13", "cback.conf.14", "cback.conf.15", "cback.conf.16", "cback.conf.17", "cback.conf.18", "cback.conf.19", "cback.conf.20", "cback.conf.21", "cback.conf.22", "cback.conf.23", ] ####################################################################### # Test Case Classes ####################################################################### ########################## # TestByteQuantity class ########################## # noinspection PyTypeChecker class TestByteQuantity(unittest.TestCase): """Tests for the ByteQuantity class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = ByteQuantity() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ quantity = ByteQuantity() self.assertEqual(None, quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(0.0, quantity.bytes) def testConstructor_002a(self): """ Test constructor with all values filled in, with valid string quantity. """ quantity = ByteQuantity("6", UNIT_BYTES) self.assertEqual("6", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(6.0, quantity.bytes) quantity = ByteQuantity("2684354560", UNIT_BYTES) self.assertEqual("2684354560", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(2684354560.0, quantity.bytes) quantity = ByteQuantity("629145600", UNIT_BYTES) self.assertEqual("629145600", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(629145600.0, quantity.bytes) quantity = ByteQuantity("2.5", UNIT_GBYTES) self.assertEqual("2.5", quantity.quantity) self.assertEqual(UNIT_GBYTES, quantity.units) self.assertEqual(2684354560.0, quantity.bytes) quantity = ByteQuantity("600", UNIT_MBYTES) self.assertEqual("600", quantity.quantity) self.assertEqual(UNIT_MBYTES, quantity.units) self.assertEqual(629145600.0, quantity.bytes) def testConstructor_002b(self): """ Test constructor with all values filled in, with valid integer quantity. """ quantity = ByteQuantity(6, UNIT_BYTES) self.assertEqual("6", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(6.0, quantity.bytes) quantity = ByteQuantity(2684354560, UNIT_BYTES) self.assertEqual("2684354560", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(2684354560.0, quantity.bytes) quantity = ByteQuantity(629145600, UNIT_BYTES) self.assertEqual("629145600", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(629145600.0, quantity.bytes) quantity = ByteQuantity(600, UNIT_MBYTES) self.assertEqual("600", quantity.quantity) self.assertEqual(UNIT_MBYTES, quantity.units) self.assertEqual(629145600.0, quantity.bytes) def testConstructor_002c(self): """ Test constructor with all values filled in, with valid float quantity. """ quantity = ByteQuantity(6.0, UNIT_BYTES) self.assertEqual("6.0", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(6.0, quantity.bytes) quantity = ByteQuantity(2684354560.0, UNIT_BYTES) self.assertEqual("2684354560.0", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(2684354560.0, quantity.bytes) quantity = ByteQuantity(629145600.0, UNIT_BYTES) self.assertEqual("629145600.0", quantity.quantity) self.assertEqual(UNIT_BYTES, quantity.units) self.assertEqual(629145600.0, quantity.bytes) quantity = ByteQuantity(2.5, UNIT_GBYTES) self.assertEqual("2.5", quantity.quantity) self.assertEqual(UNIT_GBYTES, quantity.units) self.assertEqual(2684354560.0, quantity.bytes) quantity = ByteQuantity(600.0, UNIT_MBYTES) self.assertEqual("600.0", quantity.quantity) self.assertEqual(UNIT_MBYTES, quantity.units) self.assertEqual(629145600.0, quantity.bytes) def testConstructor_003(self): """ Test assignment of quantity attribute, None value. """ quantity = ByteQuantity(quantity="1.0") self.assertEqual("1.0", quantity.quantity) self.assertEqual(1.0, quantity.bytes) quantity.quantity = None self.assertEqual(None, quantity.quantity) self.assertEqual(0.0, quantity.bytes) def testConstructor_004a(self): """ Test assignment of quantity attribute, valid string values. """ quantity = ByteQuantity() quantity.units = UNIT_BYTES # so we can test the bytes attribute self.assertEqual(None, quantity.quantity) self.assertEqual(0.0, quantity.bytes) quantity.quantity = "1.0" self.assertEqual("1.0", quantity.quantity) self.assertEqual(1.0, quantity.bytes) quantity.quantity = ".1" self.assertEqual(".1", quantity.quantity) self.assertEqual(0.1, quantity.bytes) quantity.quantity = "12" self.assertEqual("12", quantity.quantity) self.assertEqual(12.0, quantity.bytes) quantity.quantity = "0.5" self.assertEqual("0.5", quantity.quantity) self.assertEqual(0.5, quantity.bytes) quantity.quantity = "181281" self.assertEqual("181281", quantity.quantity) self.assertEqual(181281.0, quantity.bytes) quantity.quantity = "1E6" self.assertEqual("1E6", quantity.quantity) self.assertEqual(1.0e6, quantity.bytes) quantity.quantity = "0.25E2" self.assertEqual("0.25E2", quantity.quantity) self.assertEqual(0.25e2, quantity.bytes) def testConstructor_004b(self): """ Test assignment of quantity attribute, valid integer values. """ quantity = ByteQuantity() quantity.units = UNIT_BYTES # so we can test the bytes attribute quantity.quantity = 1 self.assertEqual("1", quantity.quantity) self.assertEqual(1.0, quantity.bytes) quantity.quantity = 12 self.assertEqual("12", quantity.quantity) self.assertEqual(12.0, quantity.bytes) quantity.quantity = 181281 self.assertEqual("181281", quantity.quantity) self.assertEqual(181281.0, quantity.bytes) def testConstructor_004c(self): """ Test assignment of quantity attribute, valid float values. """ quantity = ByteQuantity() quantity.units = UNIT_BYTES # so we can test the bytes attribute quantity.quantity = 1.0 self.assertEqual("1.0", quantity.quantity) self.assertEqual(1.0, quantity.bytes) quantity.quantity = 0.1 self.assertEqual("0.1", quantity.quantity) self.assertEqual(0.1, quantity.bytes) quantity.quantity = "12.0" self.assertEqual("12.0", quantity.quantity) self.assertEqual(12.0, quantity.bytes) quantity.quantity = 0.5 self.assertEqual("0.5", quantity.quantity) self.assertEqual(0.5, quantity.bytes) quantity.quantity = "181281.0" self.assertEqual("181281.0", quantity.quantity) self.assertEqual(181281.0, quantity.bytes) quantity.quantity = 1e6 self.assertEqual("1000000.0", quantity.quantity) self.assertEqual(1.0e6, quantity.bytes) quantity.quantity = 0.25e2 self.assertEqual("25.0", quantity.quantity) self.assertEqual(0.25e2, quantity.bytes) def testConstructor_005(self): """ Test assignment of quantity attribute, invalid value (empty). """ quantity = ByteQuantity() self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "") self.assertEqual(None, quantity.quantity) def testConstructor_006(self): """ Test assignment of quantity attribute, invalid value (not interpretable as a float). """ quantity = ByteQuantity() self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "blech") self.assertEqual(None, quantity.quantity) def testConstructor_007(self): """ Test assignment of quantity attribute, invalid value (negative number). """ quantity = ByteQuantity() self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-3") self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-6.8") self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-0.2") self.assertEqual(None, quantity.quantity) self.failUnlessAssignRaises(ValueError, quantity, "quantity", "-.1") self.assertEqual(None, quantity.quantity) def testConstructor_008(self): """ Test assignment of units attribute, None value. """ quantity = ByteQuantity(units=UNIT_MBYTES) self.assertEqual(UNIT_MBYTES, quantity.units) quantity.units = None self.assertEqual(UNIT_BYTES, quantity.units) def testConstructor_009(self): """ Test assignment of units attribute, valid values. """ quantity = ByteQuantity() self.assertEqual(UNIT_BYTES, quantity.units) quantity.units = UNIT_KBYTES self.assertEqual(UNIT_KBYTES, quantity.units) quantity.units = UNIT_MBYTES self.assertEqual(UNIT_MBYTES, quantity.units) quantity.units = UNIT_GBYTES self.assertEqual(UNIT_GBYTES, quantity.units) quantity.units = UNIT_BYTES self.assertEqual(UNIT_BYTES, quantity.units) def testConstructor_010(self): """ Test assignment of units attribute, invalid value (empty). """ quantity = ByteQuantity() self.assertEqual(UNIT_BYTES, quantity.units) self.failUnlessAssignRaises(ValueError, quantity, "units", "") self.assertEqual(UNIT_BYTES, quantity.units) def testConstructor_011(self): """ Test assignment of units attribute, invalid value (not a valid unit). """ quantity = ByteQuantity() self.assertEqual(UNIT_BYTES, quantity.units) self.failUnlessAssignRaises(ValueError, quantity, "units", 16) self.assertEqual(UNIT_BYTES, quantity.units) self.failUnlessAssignRaises(ValueError, quantity, "units", -2) self.assertEqual(UNIT_BYTES, quantity.units) self.failUnlessAssignRaises(ValueError, quantity, "units", "bytes") self.assertEqual(UNIT_BYTES, quantity.units) self.failUnlessAssignRaises(ValueError, quantity, "units", "B") self.assertEqual(UNIT_BYTES, quantity.units) self.failUnlessAssignRaises(ValueError, quantity, "units", "KB") self.assertEqual(UNIT_BYTES, quantity.units) self.failUnlessAssignRaises(ValueError, quantity, "units", "MB") self.assertEqual(UNIT_BYTES, quantity.units) self.failUnlessAssignRaises(ValueError, quantity, "units", "GB") self.assertEqual(UNIT_BYTES, quantity.units) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ quantity1 = ByteQuantity() quantity2 = ByteQuantity() self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ quantity1 = ByteQuantity("12", UNIT_BYTES) quantity2 = ByteQuantity("12", UNIT_BYTES) self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_003(self): """ Test comparison of two differing objects, quantity differs (one None). """ quantity1 = ByteQuantity() quantity2 = ByteQuantity(quantity="12") self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_004a(self): """ Test comparison of two differing objects, quantity differs (same units). """ quantity1 = ByteQuantity("10", UNIT_BYTES) quantity2 = ByteQuantity("12", UNIT_BYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_004b(self): """ Test comparison of two differing objects, quantity differs (different units). """ quantity1 = ByteQuantity("10", UNIT_BYTES) quantity2 = ByteQuantity("12", UNIT_KBYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_004c(self): """ Test comparison of two differing objects, quantity differs (implied UNIT_BYTES). """ quantity1 = ByteQuantity("10") quantity2 = ByteQuantity("12", UNIT_BYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_004d(self): """ Test comparison of two differing objects, quantity differs (implied UNIT_BYTES). """ quantity1 = ByteQuantity("10", UNIT_BYTES) quantity2 = ByteQuantity("12") self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_004e(self): """ Test comparison of two differing objects, quantity differs (implied UNIT_BYTES). """ quantity1 = ByteQuantity("10") quantity2 = ByteQuantity("12") self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_005(self): """ Test comparison of two differing objects, units differs (one None). """ quantity1 = ByteQuantity() quantity2 = ByteQuantity(units=UNIT_MBYTES) self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_006(self): """ Test comparison of two differing objects, units differs. """ quantity1 = ByteQuantity("12", UNIT_BYTES) quantity2 = ByteQuantity("12", UNIT_KBYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_007a(self): """ Test comparison of byte quantity to integer bytes, equivalent """ quantity1 = 12 quantity2 = ByteQuantity(quantity="12", units=UNIT_BYTES) self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_007b(self): """ Test comparison of byte quantity to integer bytes, equivalent """ quantity1 = 629145600 quantity2 = ByteQuantity(quantity="600", units=UNIT_MBYTES) self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_007c(self): """ Test comparison of byte quantity to integer bytes, equivalent """ quantity1 = ByteQuantity(quantity="600", units=UNIT_MBYTES) quantity2 = 629145600 self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_008a(self): """ Test comparison of byte quantity to integer bytes, integer smaller """ quantity1 = 11 quantity2 = ByteQuantity(quantity="12", units=UNIT_BYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_008b(self): """ Test comparison of byte quantity to integer bytes, integer smaller """ quantity1 = 130390425 quantity2 = ByteQuantity(quantity="600", units=UNIT_MBYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_009a(self): """ Test comparison of byte quantity to integer bytes, integer larger """ quantity1 = 13 quantity2 = ByteQuantity(quantity="12", units=UNIT_BYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(not quantity1 <= quantity2) self.assertTrue(quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_009b(self): """ Test comparison of byte quantity to integer bytes, integer larger """ quantity1 = ByteQuantity(quantity="600", units=UNIT_MBYTES) quantity2 = 629145610 self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_010a(self): """ Test comparison of byte quantity to float bytes, equivalent """ quantity1 = 12.0 quantity2 = ByteQuantity(quantity="12.0", units=UNIT_BYTES) self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_010b(self): """ Test comparison of byte quantity to float bytes, equivalent """ quantity1 = 629145600.0 quantity2 = ByteQuantity(quantity="600", units=UNIT_MBYTES) self.assertEqual(quantity1, quantity2) self.assertTrue(quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(not quantity1 != quantity2) def testComparison_011a(self): """ Test comparison of byte quantity to float bytes, float smaller """ quantity1 = 11.0 quantity2 = ByteQuantity(quantity="12.0", units=UNIT_BYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_011b(self): """ Test comparison of byte quantity to float bytes, float smaller """ quantity1 = 130390425.0 quantity2 = ByteQuantity(quantity="600", units=UNIT_MBYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_012a(self): """ Test comparison of byte quantity to float bytes, float larger """ quantity1 = 13.0 quantity2 = ByteQuantity(quantity="12.0", units=UNIT_BYTES) self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(not quantity1 < quantity2) self.assertTrue(not quantity1 <= quantity2) self.assertTrue(quantity1 > quantity2) self.assertTrue(quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) def testComparison_012b(self): """ Test comparison of byte quantity to float bytes, float larger """ quantity1 = ByteQuantity(quantity="600", units=UNIT_MBYTES) quantity2 = 629145610.0 self.assertNotEqual(quantity1, quantity2) self.assertTrue(not quantity1 == quantity2) self.assertTrue(quantity1 < quantity2) self.assertTrue(quantity1 <= quantity2) self.assertTrue(not quantity1 > quantity2) self.assertTrue(not quantity1 >= quantity2) self.assertTrue(quantity1 != quantity2) ############################### # TestActionDependencies class ############################### class TestActionDependencies(unittest.TestCase): """Tests for the ActionDependencies class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = ActionDependencies() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ dependencies = ActionDependencies() self.assertEqual(None, dependencies.beforeList) self.assertEqual(None, dependencies.afterList) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ dependencies = ActionDependencies(["b"], ["a"]) self.assertEqual(["b"], dependencies.beforeList) self.assertEqual(["a"], dependencies.afterList) def testConstructor_003(self): """ Test assignment of beforeList attribute, None value. """ dependencies = ActionDependencies(beforeList=[]) self.assertEqual([], dependencies.beforeList) dependencies.beforeList = None self.assertEqual(None, dependencies.beforeList) def testConstructor_004(self): """ Test assignment of beforeList attribute, empty list. """ dependencies = ActionDependencies() self.assertEqual(None, dependencies.beforeList) dependencies.beforeList = [] self.assertEqual([], dependencies.beforeList) def testConstructor_005(self): """ Test assignment of beforeList attribute, non-empty list, valid values. """ dependencies = ActionDependencies() self.assertEqual(None, dependencies.beforeList) dependencies.beforeList = [ "a", "b", ] self.assertEqual(["a", "b"], dependencies.beforeList) def testConstructor_006(self): """ Test assignment of beforeList attribute, non-empty list, invalid value. """ dependencies = ActionDependencies() self.assertEqual(None, dependencies.beforeList) self.failUnlessAssignRaises(ValueError, dependencies, "beforeList", ["KEN"]) self.assertEqual(None, dependencies.beforeList) self.failUnlessAssignRaises(ValueError, dependencies, "beforeList", ["hello, world"]) self.assertEqual(None, dependencies.beforeList) self.failUnlessAssignRaises(ValueError, dependencies, "beforeList", ["dash-word"]) self.assertEqual(None, dependencies.beforeList) self.failUnlessAssignRaises(ValueError, dependencies, "beforeList", [""]) self.assertEqual(None, dependencies.beforeList) self.failUnlessAssignRaises(ValueError, dependencies, "beforeList", [None]) self.assertEqual(None, dependencies.beforeList) def testConstructor_007(self): """ Test assignment of beforeList attribute, non-empty list, mixed values. """ dependencies = ActionDependencies() self.assertEqual(None, dependencies.beforeList) self.failUnlessAssignRaises(ValueError, dependencies, "beforeList", ["ken", "dash-word"]) def testConstructor_008(self): """ Test assignment of afterList attribute, None value. """ dependencies = ActionDependencies(afterList=[]) self.assertEqual([], dependencies.afterList) dependencies.afterList = None self.assertEqual(None, dependencies.afterList) def testConstructor_009(self): """ Test assignment of afterList attribute, non-empty list, valid values. """ dependencies = ActionDependencies() self.assertEqual(None, dependencies.afterList) dependencies.afterList = [ "a", "b", ] self.assertEqual(["a", "b"], dependencies.afterList) def testConstructor_010(self): """ Test assignment of afterList attribute, non-empty list, invalid values. """ dependencies = ActionDependencies() self.assertEqual(None, dependencies.afterList) def testConstructor_011(self): """ Test assignment of afterList attribute, non-empty list, mixed values. """ dependencies = ActionDependencies() self.assertEqual(None, dependencies.afterList) self.failUnlessAssignRaises(ValueError, dependencies, "afterList", ["KEN"]) self.assertEqual(None, dependencies.afterList) self.failUnlessAssignRaises(ValueError, dependencies, "afterList", ["hello, world"]) self.assertEqual(None, dependencies.afterList) self.failUnlessAssignRaises(ValueError, dependencies, "afterList", ["dash-word"]) self.assertEqual(None, dependencies.afterList) self.failUnlessAssignRaises(ValueError, dependencies, "afterList", [""]) self.assertEqual(None, dependencies.afterList) self.failUnlessAssignRaises(ValueError, dependencies, "afterList", [None]) self.assertEqual(None, dependencies.afterList) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ dependencies1 = ActionDependencies() dependencies2 = ActionDependencies() self.assertEqual(dependencies1, dependencies2) self.assertTrue(dependencies1 == dependencies2) self.assertTrue(not dependencies1 < dependencies2) self.assertTrue(dependencies1 <= dependencies2) self.assertTrue(not dependencies1 > dependencies2) self.assertTrue(dependencies1 >= dependencies2) self.assertTrue(not dependencies1 != dependencies2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ dependencies1 = ActionDependencies(beforeList=["a"], afterList=["b"]) dependencies2 = ActionDependencies(beforeList=["a"], afterList=["b"]) self.assertEqual(dependencies1, dependencies2) self.assertTrue(dependencies1 == dependencies2) self.assertTrue(not dependencies1 < dependencies2) self.assertTrue(dependencies1 <= dependencies2) self.assertTrue(not dependencies1 > dependencies2) self.assertTrue(dependencies1 >= dependencies2) self.assertTrue(not dependencies1 != dependencies2) def testComparison_003(self): """ Test comparison of two differing objects, beforeList differs (one None). """ dependencies1 = ActionDependencies(beforeList=None, afterList=["b"]) dependencies2 = ActionDependencies(beforeList=["a"], afterList=["b"]) self.assertTrue(not dependencies1 == dependencies2) self.assertTrue(dependencies1 < dependencies2) self.assertTrue(dependencies1 <= dependencies2) self.assertTrue(not dependencies1 > dependencies2) self.assertTrue(not dependencies1 >= dependencies2) self.assertTrue(dependencies1 != dependencies2) def testComparison_004(self): """ Test comparison of two differing objects, beforeList differs (one empty). """ dependencies1 = ActionDependencies(beforeList=[], afterList=["b"]) dependencies2 = ActionDependencies(beforeList=["a"], afterList=["b"]) self.assertTrue(not dependencies1 == dependencies2) self.assertTrue(dependencies1 < dependencies2) self.assertTrue(dependencies1 <= dependencies2) self.assertTrue(not dependencies1 > dependencies2) self.assertTrue(not dependencies1 >= dependencies2) self.assertTrue(dependencies1 != dependencies2) def testComparison_005(self): """ Test comparison of two differing objects, beforeList differs. """ dependencies1 = ActionDependencies(beforeList=["a"], afterList=["b"]) dependencies2 = ActionDependencies(beforeList=["b"], afterList=["b"]) self.assertTrue(not dependencies1 == dependencies2) self.assertTrue(dependencies1 < dependencies2) self.assertTrue(dependencies1 <= dependencies2) self.assertTrue(not dependencies1 > dependencies2) self.assertTrue(not dependencies1 >= dependencies2) self.assertTrue(dependencies1 != dependencies2) def testComparison_006(self): """ Test comparison of two differing objects, afterList differs (one None). """ dependencies1 = ActionDependencies(beforeList=["a"], afterList=["b"]) dependencies2 = ActionDependencies(beforeList=["a"], afterList=None) self.assertNotEqual(dependencies1, dependencies2) self.assertTrue(not dependencies1 == dependencies2) self.assertTrue(not dependencies1 < dependencies2) self.assertTrue(not dependencies1 <= dependencies2) self.assertTrue(dependencies1 > dependencies2) self.assertTrue(dependencies1 >= dependencies2) self.assertTrue(dependencies1 != dependencies2) def testComparison_007(self): """ Test comparison of two differing objects, afterList differs (one empty). """ dependencies1 = ActionDependencies(beforeList=["a"], afterList=["b"]) dependencies2 = ActionDependencies(beforeList=["a"], afterList=[]) self.assertNotEqual(dependencies1, dependencies2) self.assertTrue(not dependencies1 == dependencies2) self.assertTrue(not dependencies1 < dependencies2) self.assertTrue(not dependencies1 <= dependencies2) self.assertTrue(dependencies1 > dependencies2) self.assertTrue(dependencies1 >= dependencies2) self.assertTrue(dependencies1 != dependencies2) def testComparison_008(self): """ Test comparison of two differing objects, afterList differs. """ dependencies1 = ActionDependencies(beforeList=["a"], afterList=["b"]) dependencies2 = ActionDependencies(beforeList=["a"], afterList=["a"]) self.assertNotEqual(dependencies1, dependencies2) self.assertTrue(not dependencies1 == dependencies2) self.assertTrue(not dependencies1 < dependencies2) self.assertTrue(not dependencies1 <= dependencies2) self.assertTrue(dependencies1 > dependencies2) self.assertTrue(dependencies1 >= dependencies2) self.assertTrue(dependencies1 != dependencies2) ####################### # TestActionHook class ####################### class TestActionHook(unittest.TestCase): """Tests for the ActionHook class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = ActionHook() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ hook = ActionHook() self.assertEqual(False, hook._before) self.assertEqual(False, hook._after) self.assertEqual(None, hook.action) self.assertEqual(None, hook.command) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ hook = ActionHook(action="action", command="command") self.assertEqual(False, hook._before) self.assertEqual(False, hook._after) self.assertEqual("action", hook.action) self.assertEqual("command", hook.command) def testConstructor_003(self): """ Test assignment of action attribute, None value. """ hook = ActionHook(action="action") self.assertEqual("action", hook.action) hook.action = None self.assertEqual(None, hook.action) def testConstructor_004(self): """ Test assignment of action attribute, valid value. """ hook = ActionHook() self.assertEqual(None, hook.action) hook.action = "action" self.assertEqual("action", hook.action) def testConstructor_005(self): """ Test assignment of action attribute, invalid value. """ hook = ActionHook() self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "KEN") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "dash-word") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "hello, world") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "") self.assertEqual(None, hook.action) def testConstructor_006(self): """ Test assignment of command attribute, None value. """ hook = ActionHook(command="command") self.assertEqual("command", hook.command) hook.command = None self.assertEqual(None, hook.command) def testConstructor_007(self): """ Test assignment of command attribute, valid valid. """ hook = ActionHook() self.assertEqual(None, hook.command) hook.command = "command" self.assertEqual("command", hook.command) def testConstructor_008(self): """ Test assignment of command attribute, invalid valid. """ hook = ActionHook() self.assertEqual(None, hook.command) self.failUnlessAssignRaises(ValueError, hook, "command", "") self.assertEqual(None, hook.command) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ hook1 = ActionHook() hook2 = ActionHook() self.assertEqual(hook1, hook2) self.assertTrue(hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(not hook1 != hook2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ hook1 = ActionHook(action="action", command="command") hook2 = ActionHook(action="action", command="command") self.assertEqual(hook1, hook2) self.assertTrue(hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(not hook1 != hook2) def testComparison_003(self): """ Test comparison of two different objects, action differs (one None). """ hook1 = ActionHook(action="action", command="command") hook2 = ActionHook(action=None, command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(not hook1 <= hook2) self.assertTrue(hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_004(self): """ Test comparison of two different objects, action differs. """ hook1 = ActionHook(action="action2", command="command") hook2 = ActionHook(action="action1", command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(not hook1 <= hook2) self.assertTrue(hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_005(self): """ Test comparison of two different objects, command differs (one None). """ hook1 = ActionHook(action="action", command=None) hook2 = ActionHook(action="action", command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(not hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_006(self): """ Test comparison of two different objects, command differs. """ hook1 = ActionHook(action="action", command="command1") hook2 = ActionHook(action="action", command="command2") self.assertTrue(not hook1 == hook2) self.assertTrue(hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(not hook1 >= hook2) self.assertTrue(hook1 != hook2) ########################## # TestPreActionHook class ########################## class TestPreActionHook(unittest.TestCase): """Tests for the PreActionHook class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = PreActionHook() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ hook = PreActionHook() self.assertEqual(True, hook._before) self.assertEqual(False, hook._after) self.assertEqual(None, hook.action) self.assertEqual(None, hook.command) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ hook = PreActionHook(action="action", command="command") self.assertEqual(True, hook._before) self.assertEqual(False, hook._after) self.assertEqual("action", hook.action) self.assertEqual("command", hook.command) def testConstructor_003(self): """ Test assignment of action attribute, None value. """ hook = PreActionHook(action="action") self.assertEqual("action", hook.action) hook.action = None self.assertEqual(None, hook.action) def testConstructor_004(self): """ Test assignment of action attribute, valid value. """ hook = PreActionHook() self.assertEqual(None, hook.action) hook.action = "action" self.assertEqual("action", hook.action) def testConstructor_005(self): """ Test assignment of action attribute, invalid value. """ hook = PreActionHook() self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "KEN") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "dash-word") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "hello, world") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "") self.assertEqual(None, hook.action) def testConstructor_006(self): """ Test assignment of command attribute, None value. """ hook = PreActionHook(command="command") self.assertEqual("command", hook.command) hook.command = None self.assertEqual(None, hook.command) def testConstructor_007(self): """ Test assignment of command attribute, valid valid. """ hook = PreActionHook() self.assertEqual(None, hook.command) hook.command = "command" self.assertEqual("command", hook.command) def testConstructor_008(self): """ Test assignment of command attribute, invalid valid. """ hook = PreActionHook() self.assertEqual(None, hook.command) self.failUnlessAssignRaises(ValueError, hook, "command", "") self.assertEqual(None, hook.command) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ hook1 = PreActionHook() hook2 = PreActionHook() self.assertEqual(hook1, hook2) self.assertTrue(hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(not hook1 != hook2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ hook1 = PreActionHook(action="action", command="command") hook2 = PreActionHook(action="action", command="command") self.assertEqual(hook1, hook2) self.assertTrue(hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(not hook1 != hook2) def testComparison_003(self): """ Test comparison of two different objects, action differs (one None). """ hook1 = PreActionHook(action="action", command="command") hook2 = PreActionHook(action=None, command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(not hook1 <= hook2) self.assertTrue(hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_004(self): """ Test comparison of two different objects, action differs. """ hook1 = PreActionHook(action="action2", command="command") hook2 = PreActionHook(action="action1", command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(not hook1 <= hook2) self.assertTrue(hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_005(self): """ Test comparison of two different objects, command differs (one None). """ hook1 = PreActionHook(action="action", command=None) hook2 = PreActionHook(action="action", command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(not hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_006(self): """ Test comparison of two different objects, command differs. """ hook1 = PreActionHook(action="action", command="command1") hook2 = PreActionHook(action="action", command="command2") self.assertTrue(not hook1 == hook2) self.assertTrue(hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(not hook1 >= hook2) self.assertTrue(hook1 != hook2) ########################### # TestPostActionHook class ########################### class TestPostActionHook(unittest.TestCase): """Tests for the PostActionHook class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = PostActionHook() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ hook = PostActionHook() self.assertEqual(False, hook._before) self.assertEqual(True, hook._after) self.assertEqual(None, hook.action) self.assertEqual(None, hook.command) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ hook = PostActionHook(action="action", command="command") self.assertEqual(False, hook._before) self.assertEqual(True, hook._after) self.assertEqual("action", hook.action) self.assertEqual("command", hook.command) def testConstructor_003(self): """ Test assignment of action attribute, None value. """ hook = PostActionHook(action="action") self.assertEqual("action", hook.action) hook.action = None self.assertEqual(None, hook.action) def testConstructor_004(self): """ Test assignment of action attribute, valid value. """ hook = PostActionHook() self.assertEqual(None, hook.action) hook.action = "action" self.assertEqual("action", hook.action) def testConstructor_005(self): """ Test assignment of action attribute, invalid value. """ hook = PostActionHook() self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "KEN") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "dash-word") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "hello, world") self.assertEqual(None, hook.action) self.failUnlessAssignRaises(ValueError, hook, "action", "") self.assertEqual(None, hook.action) def testConstructor_006(self): """ Test assignment of command attribute, None value. """ hook = PostActionHook(command="command") self.assertEqual("command", hook.command) hook.command = None self.assertEqual(None, hook.command) def testConstructor_007(self): """ Test assignment of command attribute, valid valid. """ hook = PostActionHook() self.assertEqual(None, hook.command) hook.command = "command" self.assertEqual("command", hook.command) def testConstructor_008(self): """ Test assignment of command attribute, invalid valid. """ hook = PostActionHook() self.assertEqual(None, hook.command) self.failUnlessAssignRaises(ValueError, hook, "command", "") self.assertEqual(None, hook.command) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ hook1 = PostActionHook() hook2 = PostActionHook() self.assertEqual(hook1, hook2) self.assertTrue(hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(not hook1 != hook2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ hook1 = PostActionHook(action="action", command="command") hook2 = PostActionHook(action="action", command="command") self.assertEqual(hook1, hook2) self.assertTrue(hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(not hook1 != hook2) def testComparison_003(self): """ Test comparison of two different objects, action differs (one None). """ hook1 = PostActionHook(action="action", command="command") hook2 = PostActionHook(action=None, command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(not hook1 <= hook2) self.assertTrue(hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_004(self): """ Test comparison of two different objects, action differs. """ hook1 = PostActionHook(action="action2", command="command") hook2 = PostActionHook(action="action1", command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(not hook1 < hook2) self.assertTrue(not hook1 <= hook2) self.assertTrue(hook1 > hook2) self.assertTrue(hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_005(self): """ Test comparison of two different objects, command differs (one None). """ hook1 = PostActionHook(action="action", command=None) hook2 = PostActionHook(action="action", command="command") self.assertTrue(not hook1 == hook2) self.assertTrue(hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(not hook1 >= hook2) self.assertTrue(hook1 != hook2) def testComparison_006(self): """ Test comparison of two different objects, command differs. """ hook1 = PostActionHook(action="action", command="command1") hook2 = PostActionHook(action="action", command="command2") self.assertTrue(not hook1 == hook2) self.assertTrue(hook1 < hook2) self.assertTrue(hook1 <= hook2) self.assertTrue(not hook1 > hook2) self.assertTrue(not hook1 >= hook2) self.assertTrue(hook1 != hook2) ########################## # TestBlankBehavior class ########################## class TestBlankBehavior(unittest.TestCase): """Tests for the BlankBehavior class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = BlankBehavior() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ behavior = BlankBehavior() self.assertEqual(None, behavior.blankMode) self.assertEqual(None, behavior.blankFactor) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ behavior = BlankBehavior(blankMode="daily", blankFactor="1.0") self.assertEqual("daily", behavior.blankMode) self.assertEqual("1.0", behavior.blankFactor) def testConstructor_003(self): """ Test assignment of blankMode, None value. """ behavior = BlankBehavior(blankMode="daily") self.assertEqual("daily", behavior.blankMode) behavior.blankMode = None self.assertEqual(None, behavior.blankMode) def testConstructor_004(self): """ Test assignment of blankMode attribute, valid value. """ behavior = BlankBehavior() self.assertEqual(None, behavior.blankMode) behavior.blankMode = "daily" self.assertEqual("daily", behavior.blankMode) behavior.blankMode = "weekly" self.assertEqual("weekly", behavior.blankMode) def testConstructor_005(self): """ Test assignment of blankFactor attribute, None value. """ behavior = BlankBehavior(blankFactor="1.3") self.assertEqual("1.3", behavior.blankFactor) behavior.blankFactor = None self.assertEqual(None, behavior.blankFactor) def testConstructor_006(self): """ Test assignment of blankFactor attribute, valid values. """ behavior = BlankBehavior() self.assertEqual(None, behavior.blankFactor) behavior.blankFactor = "1.0" self.assertEqual("1.0", behavior.blankFactor) behavior.blankFactor = ".1" self.assertEqual(".1", behavior.blankFactor) behavior.blankFactor = "12" self.assertEqual("12", behavior.blankFactor) behavior.blankFactor = "0.5" self.assertEqual("0.5", behavior.blankFactor) behavior.blankFactor = "181281" self.assertEqual("181281", behavior.blankFactor) behavior.blankFactor = "1E6" self.assertEqual("1E6", behavior.blankFactor) behavior.blankFactor = "0.25E2" self.assertEqual("0.25E2", behavior.blankFactor) def testConstructor_007(self): """ Test assignment of blankFactor attribute, invalid value (empty). """ behavior = BlankBehavior() self.assertEqual(None, behavior.blankFactor) self.failUnlessAssignRaises(ValueError, behavior, "blankFactor", "") self.assertEqual(None, behavior.blankFactor) def testConstructor_008(self): """ Test assignment of blankFactor attribute, invalid value (not a floating point number). """ behavior = BlankBehavior() self.assertEqual(None, behavior.blankFactor) self.failUnlessAssignRaises(ValueError, behavior, "blankFactor", "blech") self.assertEqual(None, behavior.blankFactor) def testConstructor_009(self): """ Test assignment of blankFactor store attribute, invalid value (negative number). """ behavior = BlankBehavior() self.assertEqual(None, behavior.blankFactor) self.failUnlessAssignRaises(ValueError, behavior, "blankFactor", "-3") self.assertEqual(None, behavior.blankFactor) self.failUnlessAssignRaises(ValueError, behavior, "blankFactor", "-6.8") self.assertEqual(None, behavior.blankFactor) self.failUnlessAssignRaises(ValueError, behavior, "blankFactor", "-0.2") self.assertEqual(None, behavior.blankFactor) self.failUnlessAssignRaises(ValueError, behavior, "blankFactor", "-.1") self.assertEqual(None, behavior.blankFactor) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ behavior1 = BlankBehavior() behavior2 = BlankBehavior() self.assertEqual(behavior1, behavior2) self.assertTrue(behavior1 == behavior2) self.assertTrue(not behavior1 < behavior2) self.assertTrue(behavior1 <= behavior2) self.assertTrue(not behavior1 > behavior2) self.assertTrue(behavior1 >= behavior2) self.assertTrue(not behavior1 != behavior2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ behavior1 = BlankBehavior(blankMode="weekly", blankFactor="1.0") behavior2 = BlankBehavior(blankMode="weekly", blankFactor="1.0") self.assertEqual(behavior1, behavior2) self.assertTrue(behavior1 == behavior2) self.assertTrue(not behavior1 < behavior2) self.assertTrue(behavior1 <= behavior2) self.assertTrue(not behavior1 > behavior2) self.assertTrue(behavior1 >= behavior2) self.assertTrue(not behavior1 != behavior2) def testComparison_003(self): """ Test comparison of two different objects, blankMode differs (one None). """ behavior1 = BlankBehavior(None, blankFactor="1.0") behavior2 = BlankBehavior(blankMode="weekly", blankFactor="1.0") self.assertTrue(not behavior1 == behavior2) self.assertTrue(behavior1 < behavior2) self.assertTrue(behavior1 <= behavior2) self.assertTrue(not behavior1 > behavior2) self.assertTrue(not behavior1 >= behavior2) self.assertTrue(behavior1 != behavior2) def testComparison_004(self): """ Test comparison of two different objects, blankMode differs. """ behavior1 = BlankBehavior(blankMode="daily", blankFactor="1.0") behavior2 = BlankBehavior(blankMode="weekly", blankFactor="1.0") self.assertTrue(not behavior1 == behavior2) self.assertTrue(behavior1 < behavior2) self.assertTrue(behavior1 <= behavior2) self.assertTrue(not behavior1 > behavior2) self.assertTrue(not behavior1 >= behavior2) self.assertTrue(behavior1 != behavior2) def testComparison_005(self): """ Test comparison of two different objects, blankFactor differs (one None). """ behavior1 = BlankBehavior(blankMode="weekly", blankFactor=None) behavior2 = BlankBehavior(blankMode="weekly", blankFactor="1.0") self.assertTrue(not behavior1 == behavior2) self.assertTrue(behavior1 < behavior2) self.assertTrue(behavior1 <= behavior2) self.assertTrue(not behavior1 > behavior2) self.assertTrue(not behavior1 >= behavior2) self.assertTrue(behavior1 != behavior2) def testComparison_006(self): """ Test comparison of two different objects, blankFactor differs. """ behavior1 = BlankBehavior(blankMode="weekly", blankFactor="0.0") behavior2 = BlankBehavior(blankMode="weekly", blankFactor="1.0") self.assertTrue(not behavior1 == behavior2) self.assertTrue(behavior1 < behavior2) self.assertTrue(behavior1 <= behavior2) self.assertTrue(not behavior1 > behavior2) self.assertTrue(not behavior1 >= behavior2) self.assertTrue(behavior1 != behavior2) ########################### # TestExtendedAction class ########################### class TestExtendedAction(unittest.TestCase): """Tests for the ExtendedAction class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = ExtendedAction() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ action = ExtendedAction() self.assertEqual(None, action.name) self.assertEqual(None, action.module) self.assertEqual(None, action.function) self.assertEqual(None, action.index) self.assertEqual(None, action.dependencies) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ action = ExtendedAction("one", "two", "three", 4, ActionDependencies()) self.assertEqual("one", action.name) self.assertEqual("two", action.module) self.assertEqual("three", action.function) self.assertEqual(4, action.index) self.assertEqual(ActionDependencies(), action.dependencies) def testConstructor_003(self): """ Test assignment of name attribute, None value. """ action = ExtendedAction(name="name") self.assertEqual("name", action.name) action.name = None self.assertEqual(None, action.name) def testConstructor_004(self): """ Test assignment of name attribute, valid value. """ action = ExtendedAction() self.assertEqual(None, action.name) action.name = "name" self.assertEqual("name", action.name) action.name = "9" self.assertEqual("9", action.name) action.name = "name99name" self.assertEqual("name99name", action.name) action.name = "12action" self.assertEqual("12action", action.name) def testConstructor_005(self): """ Test assignment of name attribute, invalid value (empty). """ action = ExtendedAction() self.assertEqual(None, action.name) self.failUnlessAssignRaises(ValueError, action, "name", "") self.assertEqual(None, action.name) def testConstructor_006(self): """ Test assignment of name attribute, invalid value (does not match valid pattern). """ action = ExtendedAction() self.assertEqual(None, action.name) self.failUnlessAssignRaises(ValueError, action, "name", "Something") self.assertEqual(None, action.name) self.failUnlessAssignRaises(ValueError, action, "name", "what_ever") self.assertEqual(None, action.name) self.failUnlessAssignRaises(ValueError, action, "name", "_BOGUS") self.assertEqual(None, action.name) self.failUnlessAssignRaises(ValueError, action, "name", "stuff-here") self.assertEqual(None, action.name) self.failUnlessAssignRaises(ValueError, action, "name", "/more/stuff") self.assertEqual(None, action.name) def testConstructor_007(self): """ Test assignment of module attribute, None value. """ action = ExtendedAction(module="module") self.assertEqual("module", action.module) action.module = None self.assertEqual(None, action.module) def testConstructor_008(self): """ Test assignment of module attribute, valid value. """ action = ExtendedAction() self.assertEqual(None, action.module) action.module = "module" self.assertEqual("module", action.module) action.module = "stuff" self.assertEqual("stuff", action.module) action.module = "stuff.something" self.assertEqual("stuff.something", action.module) action.module = "_identifier.__another.one_more__" self.assertEqual("_identifier.__another.one_more__", action.module) def testConstructor_009(self): """ Test assignment of module attribute, invalid value (empty). """ action = ExtendedAction() self.assertEqual(None, action.module) self.failUnlessAssignRaises(ValueError, action, "module", "") self.assertEqual(None, action.module) def testConstructor_010(self): """ Test assignment of module attribute, invalid value (does not match valid pattern). """ action = ExtendedAction() self.assertEqual(None, action.module) self.failUnlessAssignRaises(ValueError, action, "module", "9something") self.assertEqual(None, action.module) self.failUnlessAssignRaises(ValueError, action, "module", "_bogus.") self.assertEqual(None, action.module) self.failUnlessAssignRaises(ValueError, action, "module", "-bogus") self.assertEqual(None, action.module) self.failUnlessAssignRaises(ValueError, action, "module", "/BOGUS") self.assertEqual(None, action.module) self.failUnlessAssignRaises(ValueError, action, "module", "really._really__.___really.long.bad.path.") self.assertEqual(None, action.module) self.failUnlessAssignRaises(ValueError, action, "module", ".really._really__.___really.long.bad.path") self.assertEqual(None, action.module) def testConstructor_011(self): """ Test assignment of function attribute, None value. """ action = ExtendedAction(function="function") self.assertEqual("function", action.function) action.function = None self.assertEqual(None, action.function) def testConstructor_012(self): """ Test assignment of function attribute, valid value. """ action = ExtendedAction() self.assertEqual(None, action.function) action.function = "function" self.assertEqual("function", action.function) action.function = "_stuff" self.assertEqual("_stuff", action.function) action.function = "moreStuff9" self.assertEqual("moreStuff9", action.function) action.function = "__identifier__" self.assertEqual("__identifier__", action.function) def testConstructor_013(self): """ Test assignment of function attribute, invalid value (empty). """ action = ExtendedAction() self.assertEqual(None, action.function) self.failUnlessAssignRaises(ValueError, action, "function", "") self.assertEqual(None, action.function) def testConstructor_014(self): """ Test assignment of function attribute, invalid value (does not match valid pattern). """ action = ExtendedAction() self.assertEqual(None, action.function) self.failUnlessAssignRaises(ValueError, action, "function", "9something") self.assertEqual(None, action.function) self.failUnlessAssignRaises(ValueError, action, "function", "one.two") self.assertEqual(None, action.function) self.failUnlessAssignRaises(ValueError, action, "function", "-bogus") self.assertEqual(None, action.function) self.failUnlessAssignRaises(ValueError, action, "function", "/BOGUS") self.assertEqual(None, action.function) def testConstructor_015(self): """ Test assignment of index attribute, None value. """ action = ExtendedAction(index=1) self.assertEqual(1, action.index) action.index = None self.assertEqual(None, action.index) def testConstructor_016(self): """ Test assignment of index attribute, valid value. """ action = ExtendedAction() self.assertEqual(None, action.index) action.index = 1 self.assertEqual(1, action.index) def testConstructor_017(self): """ Test assignment of index attribute, invalid value. """ action = ExtendedAction() self.assertEqual(None, action.index) self.failUnlessAssignRaises(ValueError, action, "index", "ken") self.assertEqual(None, action.index) def testConstructor_018(self): """ Test assignment of dependencies attribute, None value. """ action = ExtendedAction(dependencies=ActionDependencies()) self.assertEqual(ActionDependencies(), action.dependencies) action.dependencies = None self.assertEqual(None, action.dependencies) def testConstructor_019(self): """ Test assignment of dependencies attribute, valid value. """ action = ExtendedAction() self.assertEqual(None, action.dependencies) action.dependencies = ActionDependencies() self.assertEqual(ActionDependencies(), action.dependencies) def testConstructor_020(self): """ Test assignment of dependencies attribute, invalid value. """ action = ExtendedAction() self.assertEqual(None, action.dependencies) self.failUnlessAssignRaises(ValueError, action, "dependencies", "ken") self.assertEqual(None, action.dependencies) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ action1 = ExtendedAction() action2 = ExtendedAction() self.assertEqual(action1, action2) self.assertTrue(action1 == action2) self.assertTrue(not action1 < action2) self.assertTrue(action1 <= action2) self.assertTrue(not action1 > action2) self.assertTrue(action1 >= action2) self.assertTrue(not action1 != action2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ action1 = ExtendedAction("one", "two", "three", 4, ActionDependencies()) action2 = ExtendedAction("one", "two", "three", 4, ActionDependencies()) self.assertTrue(action1 == action2) self.assertTrue(not action1 < action2) self.assertTrue(action1 <= action2) self.assertTrue(not action1 > action2) self.assertTrue(action1 >= action2) self.assertTrue(not action1 != action2) def testComparison_003(self): """ Test comparison of two differing objects, name differs (one None). """ action1 = ExtendedAction(name="name") action2 = ExtendedAction() self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(not action1 < action2) self.assertTrue(not action1 <= action2) self.assertTrue(action1 > action2) self.assertTrue(action1 >= action2) self.assertTrue(action1 != action2) def testComparison_004(self): """ Test comparison of two differing objects, name differs. """ action1 = ExtendedAction("name2", "two", "three", 4) action2 = ExtendedAction("name1", "two", "three", 4) self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(not action1 < action2) self.assertTrue(not action1 <= action2) self.assertTrue(action1 > action2) self.assertTrue(action1 >= action2) self.assertTrue(action1 != action2) def testComparison_005(self): """ Test comparison of two differing objects, module differs (one None). """ action1 = ExtendedAction(module="whatever") action2 = ExtendedAction() self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(not action1 < action2) self.assertTrue(not action1 <= action2) self.assertTrue(action1 > action2) self.assertTrue(action1 >= action2) self.assertTrue(action1 != action2) def testComparison_006(self): """ Test comparison of two differing objects, module differs. """ action1 = ExtendedAction("one", "MODULE", "three", 4) action2 = ExtendedAction("one", "two", "three", 4) self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(action1 < action2) self.assertTrue(action1 <= action2) self.assertTrue(not action1 > action2) self.assertTrue(not action1 >= action2) self.assertTrue(action1 != action2) def testComparison_007(self): """ Test comparison of two differing objects, function differs (one None). """ action1 = ExtendedAction(function="func1") action2 = ExtendedAction() self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(not action1 < action2) self.assertTrue(not action1 <= action2) self.assertTrue(action1 > action2) self.assertTrue(action1 >= action2) self.assertTrue(action1 != action2) def testComparison_008(self): """ Test comparison of two differing objects, function differs. """ action1 = ExtendedAction("one", "two", "func1", 4) action2 = ExtendedAction("one", "two", "func2", 4) self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(action1 < action2) self.assertTrue(action1 <= action2) self.assertTrue(not action1 > action2) self.assertTrue(not action1 >= action2) self.assertTrue(action1 != action2) def testComparison_009(self): """ Test comparison of two differing objects, index differs (one None). """ action1 = ExtendedAction() action2 = ExtendedAction(index=42) self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(action1 < action2) self.assertTrue(action1 <= action2) self.assertTrue(not action1 > action2) self.assertTrue(not action1 >= action2) self.assertTrue(action1 != action2) def testComparison_010(self): """ Test comparison of two differing objects, index differs. """ action1 = ExtendedAction("one", "two", "three", 99) action2 = ExtendedAction("one", "two", "three", 12) self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(not action1 < action2) self.assertTrue(not action1 <= action2) self.assertTrue(action1 > action2) self.assertTrue(action1 >= action2) self.assertTrue(action1 != action2) def testComparison_011(self): """ Test comparison of two differing objects, dependencies differs (one None). """ action1 = ExtendedAction() action2 = ExtendedAction(dependencies=ActionDependencies()) self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(action1 < action2) self.assertTrue(action1 <= action2) self.assertTrue(not action1 > action2) self.assertTrue(not action1 >= action2) self.assertTrue(action1 != action2) def testComparison_012(self): """ Test comparison of two differing objects, dependencies differs. """ action1 = ExtendedAction("one", "two", "three", 99, ActionDependencies(beforeList=[])) action2 = ExtendedAction("one", "two", "three", 99, ActionDependencies(beforeList=["ken"])) self.assertNotEqual(action1, action2) self.assertTrue(not action1 == action2) self.assertTrue(action1 < action2) self.assertTrue(action1 <= action2) self.assertTrue(not action1 > action2) self.assertTrue(not action1 >= action2) self.assertTrue(action1 != action2) ############################ # TestCommandOverride class ############################ class TestCommandOverride(unittest.TestCase): """Tests for the CommandOverride class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = CommandOverride() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ override = CommandOverride() self.assertEqual(None, override.command) self.assertEqual(None, override.absolutePath) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ override = CommandOverride(command="command", absolutePath="/path/to/something") self.assertEqual("command", override.command) self.assertEqual("/path/to/something", override.absolutePath) def testConstructor_003(self): """ Test assignment of command attribute, None value. """ override = CommandOverride(command="command") self.assertEqual("command", override.command) override.command = None self.assertEqual(None, override.command) def testConstructor_004(self): """ Test assignment of command attribute, valid value. """ override = CommandOverride() self.assertEqual(None, override.command) override.command = "command" self.assertEqual("command", override.command) def testConstructor_005(self): """ Test assignment of command attribute, invalid value. """ override = CommandOverride() override.command = None self.failUnlessAssignRaises(ValueError, override, "command", "") override.command = None def testConstructor_006(self): """ Test assignment of absolutePath attribute, None value. """ override = CommandOverride(absolutePath="/path/to/something") self.assertEqual("/path/to/something", override.absolutePath) override.absolutePath = None self.assertEqual(None, override.absolutePath) def testConstructor_007(self): """ Test assignment of absolutePath attribute, valid value. """ override = CommandOverride() self.assertEqual(None, override.absolutePath) override.absolutePath = "/path/to/something" self.assertEqual("/path/to/something", override.absolutePath) def testConstructor_008(self): """ Test assignment of absolutePath attribute, invalid value. """ override = CommandOverride() override.command = None self.failUnlessAssignRaises(ValueError, override, "absolutePath", "path/to/something/relative") override.command = None self.failUnlessAssignRaises(ValueError, override, "absolutePath", "") override.command = None ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ override1 = CommandOverride() override2 = CommandOverride() self.assertEqual(override1, override2) self.assertTrue(override1 == override2) self.assertTrue(not override1 < override2) self.assertTrue(override1 <= override2) self.assertTrue(not override1 > override2) self.assertTrue(override1 >= override2) self.assertTrue(not override1 != override2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ override1 = CommandOverride(command="command", absolutePath="/path/to/something") override2 = CommandOverride(command="command", absolutePath="/path/to/something") self.assertEqual(override1, override2) self.assertTrue(override1 == override2) self.assertTrue(not override1 < override2) self.assertTrue(override1 <= override2) self.assertTrue(not override1 > override2) self.assertTrue(override1 >= override2) self.assertTrue(not override1 != override2) def testComparison_003(self): """ Test comparison of differing objects, command differs (one None). """ override1 = CommandOverride(command=None, absolutePath="/path/to/something") override2 = CommandOverride(command="command", absolutePath="/path/to/something") self.assertTrue(not override1 == override2) self.assertTrue(override1 < override2) self.assertTrue(override1 <= override2) self.assertTrue(not override1 > override2) self.assertTrue(not override1 >= override2) self.assertTrue(override1 != override2) def testComparison_004(self): """ Test comparison of differing objects, command differs. """ override1 = CommandOverride(command="command2", absolutePath="/path/to/something") override2 = CommandOverride(command="command1", absolutePath="/path/to/something") self.assertTrue(not override1 == override2) self.assertTrue(not override1 < override2) self.assertTrue(not override1 <= override2) self.assertTrue(override1 > override2) self.assertTrue(override1 >= override2) self.assertTrue(override1 != override2) def testComparison_005(self): """ Test comparison of differing objects, absolutePath differs (one None). """ override1 = CommandOverride(command="command", absolutePath="/path/to/something") override2 = CommandOverride(command="command", absolutePath=None) self.assertTrue(not override1 == override2) self.assertTrue(not override1 < override2) self.assertTrue(not override1 <= override2) self.assertTrue(override1 > override2) self.assertTrue(override1 >= override2) self.assertTrue(override1 != override2) def testComparison_006(self): """ Test comparison of differing objects, absolutePath differs. """ override1 = CommandOverride(command="command", absolutePath="/path/to/something1") override2 = CommandOverride(command="command", absolutePath="/path/to/something2") self.assertTrue(not override1 == override2) self.assertTrue(override1 < override2) self.assertTrue(override1 <= override2) self.assertTrue(not override1 > override2) self.assertTrue(not override1 >= override2) self.assertTrue(override1 != override2) ######################## # TestCollectFile class ######################## class TestCollectFile(unittest.TestCase): """Tests for the CollectFile class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = CollectFile() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ collectFile = CollectFile() self.assertEqual(None, collectFile.absolutePath) self.assertEqual(None, collectFile.collectMode) self.assertEqual(None, collectFile.archiveMode) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ collectFile = CollectFile("/etc/whatever", "incr", "tar") self.assertEqual("/etc/whatever", collectFile.absolutePath) self.assertEqual("incr", collectFile.collectMode) self.assertEqual("tar", collectFile.archiveMode) def testConstructor_003(self): """ Test assignment of absolutePath attribute, None value. """ collectFile = CollectFile(absolutePath="/whatever") self.assertEqual("/whatever", collectFile.absolutePath) collectFile.absolutePath = None self.assertEqual(None, collectFile.absolutePath) def testConstructor_004(self): """ Test assignment of absolutePath attribute, valid value. """ collectFile = CollectFile() self.assertEqual(None, collectFile.absolutePath) collectFile.absolutePath = "/etc/whatever" self.assertEqual("/etc/whatever", collectFile.absolutePath) def testConstructor_005(self): """ Test assignment of absolutePath attribute, invalid value (empty). """ collectFile = CollectFile() self.assertEqual(None, collectFile.absolutePath) self.failUnlessAssignRaises(ValueError, collectFile, "absolutePath", "") self.assertEqual(None, collectFile.absolutePath) def testConstructor_006(self): """ Test assignment of absolutePath attribute, invalid value (non-absolute). """ collectFile = CollectFile() self.assertEqual(None, collectFile.absolutePath) self.failUnlessAssignRaises(ValueError, collectFile, "absolutePath", "whatever") self.assertEqual(None, collectFile.absolutePath) def testConstructor_007(self): """ Test assignment of collectMode attribute, None value. """ collectFile = CollectFile(collectMode="incr") self.assertEqual("incr", collectFile.collectMode) collectFile.collectMode = None self.assertEqual(None, collectFile.collectMode) def testConstructor_008(self): """ Test assignment of collectMode attribute, valid value. """ collectFile = CollectFile() self.assertEqual(None, collectFile.collectMode) collectFile.collectMode = "daily" self.assertEqual("daily", collectFile.collectMode) collectFile.collectMode = "weekly" self.assertEqual("weekly", collectFile.collectMode) collectFile.collectMode = "incr" self.assertEqual("incr", collectFile.collectMode) def testConstructor_009(self): """ Test assignment of collectMode attribute, invalid value (empty). """ collectFile = CollectFile() self.assertEqual(None, collectFile.collectMode) self.failUnlessAssignRaises(ValueError, collectFile, "collectMode", "") self.assertEqual(None, collectFile.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ collectFile = CollectFile() self.assertEqual(None, collectFile.collectMode) self.failUnlessAssignRaises(ValueError, collectFile, "collectMode", "bogus") self.assertEqual(None, collectFile.collectMode) def testConstructor_011(self): """ Test assignment of archiveMode attribute, None value. """ collectFile = CollectFile(archiveMode="tar") self.assertEqual("tar", collectFile.archiveMode) collectFile.archiveMode = None self.assertEqual(None, collectFile.archiveMode) def testConstructor_012(self): """ Test assignment of archiveMode attribute, valid value. """ collectFile = CollectFile() self.assertEqual(None, collectFile.archiveMode) collectFile.archiveMode = "tar" self.assertEqual("tar", collectFile.archiveMode) collectFile.archiveMode = "targz" self.assertEqual("targz", collectFile.archiveMode) collectFile.archiveMode = "tarbz2" self.assertEqual("tarbz2", collectFile.archiveMode) def testConstructor_013(self): """ Test assignment of archiveMode attribute, invalid value (empty). """ collectFile = CollectFile() self.assertEqual(None, collectFile.archiveMode) self.failUnlessAssignRaises(ValueError, collectFile, "archiveMode", "") self.assertEqual(None, collectFile.archiveMode) def testConstructor_014(self): """ Test assignment of archiveMode attribute, invalid value (not in list). """ collectFile = CollectFile() self.assertEqual(None, collectFile.archiveMode) self.failUnlessAssignRaises(ValueError, collectFile, "archiveMode", "bogus") self.assertEqual(None, collectFile.archiveMode) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ collectFile1 = CollectFile() collectFile2 = CollectFile() self.assertEqual(collectFile1, collectFile2) self.assertTrue(collectFile1 == collectFile2) self.assertTrue(not collectFile1 < collectFile2) self.assertTrue(collectFile1 <= collectFile2) self.assertTrue(not collectFile1 > collectFile2) self.assertTrue(collectFile1 >= collectFile2) self.assertTrue(not collectFile1 != collectFile2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ collectFile1 = CollectFile("/etc/whatever", "incr", "tar") collectFile2 = CollectFile("/etc/whatever", "incr", "tar") self.assertTrue(collectFile1 == collectFile2) self.assertTrue(not collectFile1 < collectFile2) self.assertTrue(collectFile1 <= collectFile2) self.assertTrue(not collectFile1 > collectFile2) self.assertTrue(collectFile1 >= collectFile2) self.assertTrue(not collectFile1 != collectFile2) def testComparison_003(self): """ Test comparison of two differing objects, absolutePath differs (one None). """ collectFile1 = CollectFile() collectFile2 = CollectFile(absolutePath="/whatever") self.assertNotEqual(collectFile1, collectFile2) self.assertTrue(not collectFile1 == collectFile2) self.assertTrue(collectFile1 < collectFile2) self.assertTrue(collectFile1 <= collectFile2) self.assertTrue(not collectFile1 > collectFile2) self.assertTrue(not collectFile1 >= collectFile2) self.assertTrue(collectFile1 != collectFile2) def testComparison_004(self): """ Test comparison of two differing objects, absolutePath differs. """ collectFile1 = CollectFile("/etc/whatever", "incr", "tar") collectFile2 = CollectFile("/stuff", "incr", "tar") self.assertNotEqual(collectFile1, collectFile2) self.assertTrue(not collectFile1 == collectFile2) self.assertTrue(collectFile1 < collectFile2) self.assertTrue(collectFile1 <= collectFile2) self.assertTrue(not collectFile1 > collectFile2) self.assertTrue(not collectFile1 >= collectFile2) self.assertTrue(collectFile1 != collectFile2) def testComparison_005(self): """ Test comparison of two differing objects, collectMode differs (one None). """ collectFile1 = CollectFile() collectFile2 = CollectFile(collectMode="incr") self.assertNotEqual(collectFile1, collectFile2) self.assertTrue(not collectFile1 == collectFile2) self.assertTrue(collectFile1 < collectFile2) self.assertTrue(collectFile1 <= collectFile2) self.assertTrue(not collectFile1 > collectFile2) self.assertTrue(not collectFile1 >= collectFile2) self.assertTrue(collectFile1 != collectFile2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs. """ collectFile1 = CollectFile("/etc/whatever", "incr", "tar") collectFile2 = CollectFile("/etc/whatever", "daily", "tar") self.assertNotEqual(collectFile1, collectFile2) self.assertTrue(not collectFile1 == collectFile2) self.assertTrue(not collectFile1 < collectFile2) self.assertTrue(not collectFile1 <= collectFile2) self.assertTrue(collectFile1 > collectFile2) self.assertTrue(collectFile1 >= collectFile2) self.assertTrue(collectFile1 != collectFile2) def testComparison_007(self): """ Test comparison of two differing objects, archiveMode differs (one None). """ collectFile1 = CollectFile() collectFile2 = CollectFile(archiveMode="tar") self.assertNotEqual(collectFile1, collectFile2) self.assertTrue(not collectFile1 == collectFile2) self.assertTrue(collectFile1 < collectFile2) self.assertTrue(collectFile1 <= collectFile2) self.assertTrue(not collectFile1 > collectFile2) self.assertTrue(not collectFile1 >= collectFile2) self.assertTrue(collectFile1 != collectFile2) def testComparison_008(self): """ Test comparison of two differing objects, archiveMode differs. """ collectFile1 = CollectFile("/etc/whatever", "incr", "targz") collectFile2 = CollectFile("/etc/whatever", "incr", "tar") self.assertNotEqual(collectFile1, collectFile2) self.assertTrue(not collectFile1 == collectFile2) self.assertTrue(not collectFile1 < collectFile2) self.assertTrue(not collectFile1 <= collectFile2) self.assertTrue(collectFile1 > collectFile2) self.assertTrue(collectFile1 >= collectFile2) self.assertTrue(collectFile1 != collectFile2) ####################### # TestCollectDir class ####################### class TestCollectDir(unittest.TestCase): """Tests for the CollectDir class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = CollectDir() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ collectDir = CollectDir() self.assertEqual(None, collectDir.absolutePath) self.assertEqual(None, collectDir.collectMode) self.assertEqual(None, collectDir.archiveMode) self.assertEqual(None, collectDir.ignoreFile) self.assertEqual(None, collectDir.linkDepth) self.assertEqual(False, collectDir.dereference) self.assertEqual(None, collectDir.recursionLevel) self.assertEqual(None, collectDir.absoluteExcludePaths) self.assertEqual(None, collectDir.relativeExcludePaths) self.assertEqual(None, collectDir.excludePatterns) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ collectDir = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 2, True, 6) self.assertEqual("/etc/whatever", collectDir.absolutePath) self.assertEqual("incr", collectDir.collectMode) self.assertEqual("tar", collectDir.archiveMode) self.assertEqual(".ignore", collectDir.ignoreFile) self.assertEqual(2, collectDir.linkDepth) self.assertEqual(True, collectDir.dereference) self.assertEqual(6, collectDir.recursionLevel) self.assertEqual([], collectDir.absoluteExcludePaths) self.assertEqual([], collectDir.relativeExcludePaths) self.assertEqual([], collectDir.excludePatterns) def testConstructor_003(self): """ Test assignment of absolutePath attribute, None value. """ collectDir = CollectDir(absolutePath="/whatever") self.assertEqual("/whatever", collectDir.absolutePath) collectDir.absolutePath = None self.assertEqual(None, collectDir.absolutePath) def testConstructor_004(self): """ Test assignment of absolutePath attribute, valid value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.absolutePath) collectDir.absolutePath = "/etc/whatever" self.assertEqual("/etc/whatever", collectDir.absolutePath) def testConstructor_005(self): """ Test assignment of absolutePath attribute, invalid value (empty). """ collectDir = CollectDir() self.assertEqual(None, collectDir.absolutePath) self.failUnlessAssignRaises(ValueError, collectDir, "absolutePath", "") self.assertEqual(None, collectDir.absolutePath) def testConstructor_006(self): """ Test assignment of absolutePath attribute, invalid value (non-absolute). """ collectDir = CollectDir() self.assertEqual(None, collectDir.absolutePath) self.failUnlessAssignRaises(ValueError, collectDir, "absolutePath", "whatever") self.assertEqual(None, collectDir.absolutePath) def testConstructor_007(self): """ Test assignment of collectMode attribute, None value. """ collectDir = CollectDir(collectMode="incr") self.assertEqual("incr", collectDir.collectMode) collectDir.collectMode = None self.assertEqual(None, collectDir.collectMode) def testConstructor_008(self): """ Test assignment of collectMode attribute, valid value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.collectMode) collectDir.collectMode = "daily" self.assertEqual("daily", collectDir.collectMode) collectDir.collectMode = "weekly" self.assertEqual("weekly", collectDir.collectMode) collectDir.collectMode = "incr" self.assertEqual("incr", collectDir.collectMode) def testConstructor_009(self): """ Test assignment of collectMode attribute, invalid value (empty). """ collectDir = CollectDir() self.assertEqual(None, collectDir.collectMode) self.failUnlessAssignRaises(ValueError, collectDir, "collectMode", "") self.assertEqual(None, collectDir.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ collectDir = CollectDir() self.assertEqual(None, collectDir.collectMode) self.failUnlessAssignRaises(ValueError, collectDir, "collectMode", "bogus") self.assertEqual(None, collectDir.collectMode) def testConstructor_011(self): """ Test assignment of archiveMode attribute, None value. """ collectDir = CollectDir(archiveMode="tar") self.assertEqual("tar", collectDir.archiveMode) collectDir.archiveMode = None self.assertEqual(None, collectDir.archiveMode) def testConstructor_012(self): """ Test assignment of archiveMode attribute, valid value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.archiveMode) collectDir.archiveMode = "tar" self.assertEqual("tar", collectDir.archiveMode) collectDir.archiveMode = "targz" self.assertEqual("targz", collectDir.archiveMode) collectDir.archiveMode = "tarbz2" self.assertEqual("tarbz2", collectDir.archiveMode) def testConstructor_013(self): """ Test assignment of archiveMode attribute, invalid value (empty). """ collectDir = CollectDir() self.assertEqual(None, collectDir.archiveMode) self.failUnlessAssignRaises(ValueError, collectDir, "archiveMode", "") self.assertEqual(None, collectDir.archiveMode) def testConstructor_014(self): """ Test assignment of archiveMode attribute, invalid value (not in list). """ collectDir = CollectDir() self.assertEqual(None, collectDir.archiveMode) self.failUnlessAssignRaises(ValueError, collectDir, "archiveMode", "bogus") self.assertEqual(None, collectDir.archiveMode) def testConstructor_015(self): """ Test assignment of ignoreFile attribute, None value. """ collectDir = CollectDir(ignoreFile="ignore") self.assertEqual("ignore", collectDir.ignoreFile) collectDir.ignoreFile = None self.assertEqual(None, collectDir.ignoreFile) def testConstructor_016(self): """ Test assignment of ignoreFile attribute, valid value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.ignoreFile) collectDir.ignoreFile = "ignorefile" self.assertEqual("ignorefile", collectDir.ignoreFile) def testConstructor_017(self): """ Test assignment of ignoreFile attribute, invalid value (empty). """ collectDir = CollectDir() self.assertEqual(None, collectDir.ignoreFile) self.failUnlessAssignRaises(ValueError, collectDir, "ignoreFile", "") self.assertEqual(None, collectDir.ignoreFile) def testConstructor_018(self): """ Test assignment of absoluteExcludePaths attribute, None value. """ collectDir = CollectDir(absoluteExcludePaths=[]) self.assertEqual([], collectDir.absoluteExcludePaths) collectDir.absoluteExcludePaths = None self.assertEqual(None, collectDir.absoluteExcludePaths) def testConstructor_019(self): """ Test assignment of absoluteExcludePaths attribute, [] value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.absoluteExcludePaths) collectDir.absoluteExcludePaths = [] self.assertEqual([], collectDir.absoluteExcludePaths) def testConstructor_020(self): """ Test assignment of absoluteExcludePaths attribute, single valid entry. """ collectDir = CollectDir() self.assertEqual(None, collectDir.absoluteExcludePaths) collectDir.absoluteExcludePaths = [ "/whatever", ] self.assertEqual(["/whatever"], collectDir.absoluteExcludePaths) collectDir.absoluteExcludePaths.append("/stuff") self.assertEqual(["/whatever", "/stuff"], collectDir.absoluteExcludePaths) def testConstructor_021(self): """ Test assignment of absoluteExcludePaths attribute, multiple valid entries. """ collectDir = CollectDir() self.assertEqual(None, collectDir.absoluteExcludePaths) collectDir.absoluteExcludePaths = [ "/whatever", "/stuff", ] self.assertEqual(["/whatever", "/stuff"], collectDir.absoluteExcludePaths) collectDir.absoluteExcludePaths.append("/etc/X11") self.assertEqual(["/whatever", "/stuff", "/etc/X11"], collectDir.absoluteExcludePaths) def testConstructor_022(self): """ Test assignment of absoluteExcludePaths attribute, single invalid entry (empty). """ collectDir = CollectDir() self.assertEqual(None, collectDir.absoluteExcludePaths) self.failUnlessAssignRaises(ValueError, collectDir, "absoluteExcludePaths", [""]) self.assertEqual(None, collectDir.absoluteExcludePaths) def testConstructor_023(self): """ Test assignment of absoluteExcludePaths attribute, single invalid entry (not absolute). """ collectDir = CollectDir() self.assertEqual(None, collectDir.absoluteExcludePaths) self.failUnlessAssignRaises(ValueError, collectDir, "absoluteExcludePaths", ["notabsolute"]) self.assertEqual(None, collectDir.absoluteExcludePaths) def testConstructor_024(self): """ Test assignment of absoluteExcludePaths attribute, mixed valid and invalid entries. """ collectDir = CollectDir() self.assertEqual(None, collectDir.absoluteExcludePaths) self.failUnlessAssignRaises(ValueError, collectDir, "absoluteExcludePaths", ["/good", "bad", "/alsogood"]) self.assertEqual(None, collectDir.absoluteExcludePaths) def testConstructor_025(self): """ Test assignment of relativeExcludePaths attribute, None value. """ collectDir = CollectDir(relativeExcludePaths=[]) self.assertEqual([], collectDir.relativeExcludePaths) collectDir.relativeExcludePaths = None self.assertEqual(None, collectDir.relativeExcludePaths) def testConstructor_026(self): """ Test assignment of relativeExcludePaths attribute, [] value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.relativeExcludePaths) collectDir.relativeExcludePaths = [] self.assertEqual([], collectDir.relativeExcludePaths) def testConstructor_027(self): """ Test assignment of relativeExcludePaths attribute, single valid entry. """ collectDir = CollectDir() self.assertEqual(None, collectDir.relativeExcludePaths) collectDir.relativeExcludePaths = [ "stuff", ] self.assertEqual(["stuff"], collectDir.relativeExcludePaths) collectDir.relativeExcludePaths.insert(0, "bogus") self.assertEqual(["bogus", "stuff"], collectDir.relativeExcludePaths) def testConstructor_028(self): """ Test assignment of relativeExcludePaths attribute, multiple valid entries. """ collectDir = CollectDir() self.assertEqual(None, collectDir.relativeExcludePaths) collectDir.relativeExcludePaths = [ "bogus", "stuff", ] self.assertEqual(["bogus", "stuff"], collectDir.relativeExcludePaths) collectDir.relativeExcludePaths.append("more") self.assertEqual(["bogus", "stuff", "more"], collectDir.relativeExcludePaths) def testConstructor_029(self): """ Test assignment of excludePatterns attribute, None value. """ collectDir = CollectDir(excludePatterns=[]) self.assertEqual([], collectDir.excludePatterns) collectDir.excludePatterns = None self.assertEqual(None, collectDir.excludePatterns) def testConstructor_030(self): """ Test assignment of excludePatterns attribute, [] value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.excludePatterns) collectDir.excludePatterns = [] self.assertEqual([], collectDir.excludePatterns) def testConstructor_031(self): """ Test assignment of excludePatterns attribute, single valid entry. """ collectDir = CollectDir() self.assertEqual(None, collectDir.excludePatterns) collectDir.excludePatterns = [ "valid", ] self.assertEqual(["valid"], collectDir.excludePatterns) collectDir.excludePatterns.append("more") self.assertEqual(["valid", "more"], collectDir.excludePatterns) def testConstructor_032(self): """ Test assignment of excludePatterns attribute, multiple valid entries. """ collectDir = CollectDir() self.assertEqual(None, collectDir.excludePatterns) collectDir.excludePatterns = [ "valid", "more", ] self.assertEqual(["valid", "more"], collectDir.excludePatterns) collectDir.excludePatterns.insert(1, "bogus") self.assertEqual(["valid", "bogus", "more"], collectDir.excludePatterns) def testConstructor_033(self): """ Test assignment of excludePatterns attribute, single invalid entry. """ collectDir = CollectDir() self.assertEqual(None, collectDir.excludePatterns) self.failUnlessAssignRaises(ValueError, collectDir, "excludePatterns", ["*.jpg"]) self.assertEqual(None, collectDir.excludePatterns) def testConstructor_034(self): """ Test assignment of excludePatterns attribute, multiple invalid entries. """ collectDir = CollectDir() self.assertEqual(None, collectDir.excludePatterns) self.failUnlessAssignRaises(ValueError, collectDir, "excludePatterns", ["*.jpg", "*"]) self.assertEqual(None, collectDir.excludePatterns) def testConstructor_035(self): """ Test assignment of excludePatterns attribute, mixed valid and invalid entries. """ collectDir = CollectDir() self.assertEqual(None, collectDir.excludePatterns) self.failUnlessAssignRaises(ValueError, collectDir, "excludePatterns", ["*.jpg", "valid"]) self.assertEqual(None, collectDir.excludePatterns) def testConstructor_036(self): """ Test assignment of linkDepth attribute, None value. """ collectDir = CollectDir(linkDepth=1) self.assertEqual(1, collectDir.linkDepth) collectDir.linkDepth = None self.assertEqual(None, collectDir.linkDepth) def testConstructor_037(self): """ Test assignment of linkDepth attribute, valid value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.linkDepth) collectDir.linkDepth = 1 self.assertEqual(1, collectDir.linkDepth) def testConstructor_038(self): """ Test assignment of linkDepth attribute, invalid value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.linkDepth) self.failUnlessAssignRaises(ValueError, collectDir, "linkDepth", "ken") self.assertEqual(None, collectDir.linkDepth) def testConstructor_039(self): """ Test assignment of dereference attribute, None value. """ collectDir = CollectDir(dereference=True) self.assertEqual(True, collectDir.dereference) collectDir.dereference = None self.assertEqual(False, collectDir.dereference) def testConstructor_040(self): """ Test assignment of dereference attribute, valid value (real boolean). """ collectDir = CollectDir() self.assertEqual(False, collectDir.dereference) collectDir.dereference = True self.assertEqual(True, collectDir.dereference) collectDir.dereference = False self.assertEqual(False, collectDir.dereference) def testConstructor_041(self): """ Test assignment of dereference attribute, valid value (expression). """ collectDir = CollectDir() self.assertEqual(False, collectDir.dereference) collectDir.dereference = 0 self.assertEqual(False, collectDir.dereference) collectDir.dereference = [] self.assertEqual(False, collectDir.dereference) collectDir.dereference = None self.assertEqual(False, collectDir.dereference) collectDir.dereference = ["a"] self.assertEqual(True, collectDir.dereference) collectDir.dereference = 3 self.assertEqual(True, collectDir.dereference) def testConstructor_042(self): """ Test assignment of recursionLevel attribute, None value. """ collectDir = CollectDir(recursionLevel=1) self.assertEqual(1, collectDir.recursionLevel) collectDir.recursionLevel = None self.assertEqual(None, collectDir.recursionLevel) def testConstructor_043(self): """ Test assignment of recursionLevel attribute, valid value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.recursionLevel) collectDir.recursionLevel = 1 self.assertEqual(1, collectDir.recursionLevel) def testConstructor_044(self): """ Test assignment of recursionLevel attribute, invalid value. """ collectDir = CollectDir() self.assertEqual(None, collectDir.recursionLevel) self.failUnlessAssignRaises(ValueError, collectDir, "recursionLevel", "ken") self.assertEqual(None, collectDir.recursionLevel) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ collectDir1 = CollectDir() collectDir2 = CollectDir() self.assertEqual(collectDir1, collectDir2) self.assertTrue(collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(not collectDir1 != collectDir2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None (empty lists). """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) self.assertTrue(collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(not collectDir1 != collectDir2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None (non-empty lists). """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", ["/one"], ["two"], ["three"], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", ["/one"], ["two"], ["three"], 1, True, 6) self.assertTrue(collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(not collectDir1 != collectDir2) def testComparison_004(self): """ Test comparison of two differing objects, absolutePath differs (one None). """ collectDir1 = CollectDir() collectDir2 = CollectDir(absolutePath="/whatever") self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_005(self): """ Test comparison of two differing objects, absolutePath differs. """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/stuff", "incr", "tar", ".ignore", [], [], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs (one None). """ collectDir1 = CollectDir() collectDir2 = CollectDir(collectMode="incr") self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_007(self): """ Test comparison of two differing objects, collectMode differs. """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "daily", "tar", ".ignore", [], [], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(not collectDir1 <= collectDir2) self.assertTrue(collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_008(self): """ Test comparison of two differing objects, archiveMode differs (one None). """ collectDir1 = CollectDir() collectDir2 = CollectDir(archiveMode="tar") self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_009(self): """ Test comparison of two differing objects, archiveMode differs. """ collectDir1 = CollectDir("/etc/whatever", "incr", "targz", ".ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(not collectDir1 <= collectDir2) self.assertTrue(collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_010(self): """ Test comparison of two differing objects, ignoreFile differs (one None). """ collectDir1 = CollectDir() collectDir2 = CollectDir(ignoreFile="ignore") self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_011(self): """ Test comparison of two differing objects, ignoreFile differs. """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", "ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(not collectDir1 <= collectDir2) self.assertTrue(collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_012(self): """ Test comparison of two differing objects, absoluteExcludePaths differs (one None, one empty). """ collectDir1 = CollectDir() collectDir2 = CollectDir(absoluteExcludePaths=[]) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_013(self): """ Test comparison of two differing objects, absoluteExcludePaths differs (one None, one not empty). """ collectDir1 = CollectDir() collectDir2 = CollectDir(absoluteExcludePaths=["/whatever"]) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_014(self): """ Test comparison of two differing objects, absoluteExcludePaths differs (one empty, one not empty). """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", ["/whatever"], [], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_015(self): """ Test comparison of two differing objects, absoluteExcludePaths differs (both not empty). """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", ["/stuff"], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", ["/stuff", "/something"], [], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) # note: different than standard due to unsorted list self.assertTrue(not collectDir1 <= collectDir2) # note: different than standard due to unsorted list self.assertTrue(collectDir1 > collectDir2) # note: different than standard due to unsorted list self.assertTrue(collectDir1 >= collectDir2) # note: different than standard due to unsorted list self.assertTrue(collectDir1 != collectDir2) def testComparison_016(self): """ Test comparison of two differing objects, relativeExcludePaths differs (one None, one empty). """ collectDir1 = CollectDir() collectDir2 = CollectDir(relativeExcludePaths=[]) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_017(self): """ Test comparison of two differing objects, relativeExcludePaths differs (one None, one not empty). """ collectDir1 = CollectDir() collectDir2 = CollectDir(relativeExcludePaths=["stuff", "other"]) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_018(self): """ Test comparison of two differing objects, relativeExcludePaths differs (one empty, one not empty). """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], ["one"], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(not collectDir1 <= collectDir2) self.assertTrue(collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_019(self): """ Test comparison of two differing objects, relativeExcludePaths differs (both not empty). """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], ["one"], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], ["two"], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_020(self): """ Test comparison of two differing objects, excludePatterns differs (one None, one empty). """ collectDir1 = CollectDir() collectDir2 = CollectDir(excludePatterns=[]) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_021(self): """ Test comparison of two differing objects, excludePatterns differs (one None, one not empty). """ collectDir1 = CollectDir() collectDir2 = CollectDir(excludePatterns=["one", "two", "three"]) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_022(self): """ Test comparison of two differing objects, excludePatterns differs (one empty, one not empty). """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], ["pattern"], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_023(self): """ Test comparison of two differing objects, excludePatterns differs (both not empty). """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], ["p1"], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", ".ignore", [], [], ["p2"], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_024(self): """ Test comparison of two differing objects, linkDepth differs (one None). """ collectDir1 = CollectDir() collectDir2 = CollectDir(linkDepth=1) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_025(self): """ Test comparison of two differing objects, linkDepth differs. """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", "ignore", [], [], [], 2, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", "ignore", [], [], [], 1, True, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(not collectDir1 <= collectDir2) self.assertTrue(collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_026(self): """ Test comparison of two differing objects, dereference differs (one None). """ collectDir1 = CollectDir() collectDir2 = CollectDir(dereference=True) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_027(self): """ Test comparison of two differing objects, dereference differs. """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", "ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", "ignore", [], [], [], 1, False, 6) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(not collectDir1 <= collectDir2) self.assertTrue(collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_028(self): """ Test comparison of two differing objects, recursionLevel differs (one None). """ collectDir1 = CollectDir() collectDir2 = CollectDir(recursionLevel=1) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(collectDir1 < collectDir2) self.assertTrue(collectDir1 <= collectDir2) self.assertTrue(not collectDir1 > collectDir2) self.assertTrue(not collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) def testComparison_029(self): """ Test comparison of two differing objects, recursionLevel differs. """ collectDir1 = CollectDir("/etc/whatever", "incr", "tar", "ignore", [], [], [], 1, True, 6) collectDir2 = CollectDir("/etc/whatever", "incr", "tar", "ignore", [], [], [], 1, True, 5) self.assertNotEqual(collectDir1, collectDir2) self.assertTrue(not collectDir1 == collectDir2) self.assertTrue(not collectDir1 < collectDir2) self.assertTrue(not collectDir1 <= collectDir2) self.assertTrue(collectDir1 > collectDir2) self.assertTrue(collectDir1 >= collectDir2) self.assertTrue(collectDir1 != collectDir2) ##################### # TestPurgeDir class ##################### class TestPurgeDir(unittest.TestCase): """Tests for the PurgeDir class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = PurgeDir() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.absolutePath) self.assertEqual(None, purgeDir.retainDays) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ purgeDir = PurgeDir("/whatever", 0) self.assertEqual("/whatever", purgeDir.absolutePath) self.assertEqual(0, purgeDir.retainDays) def testConstructor_003(self): """ Test assignment of absolutePath attribute, None value. """ purgeDir = PurgeDir(absolutePath="/whatever") self.assertEqual("/whatever", purgeDir.absolutePath) purgeDir.absolutePath = None self.assertEqual(None, purgeDir.absolutePath) def testConstructor_004(self): """ Test assignment of absolutePath attribute, valid value. """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.absolutePath) purgeDir.absolutePath = "/etc/whatever" self.assertEqual("/etc/whatever", purgeDir.absolutePath) def testConstructor_005(self): """ Test assignment of absolutePath attribute, invalid value (empty). """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.absolutePath) self.failUnlessAssignRaises(ValueError, purgeDir, "absolutePath", "") self.assertEqual(None, purgeDir.absolutePath) def testConstructor_006(self): """ Test assignment of absolutePath attribute, invalid value (non-absolute). """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.absolutePath) self.failUnlessAssignRaises(ValueError, purgeDir, "absolutePath", "bogus") self.assertEqual(None, purgeDir.absolutePath) def testConstructor_007(self): """ Test assignment of retainDays attribute, None value. """ purgeDir = PurgeDir(retainDays=12) self.assertEqual(12, purgeDir.retainDays) purgeDir.retainDays = None self.assertEqual(None, purgeDir.retainDays) def testConstructor_008(self): """ Test assignment of retainDays attribute, valid value (integer). """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.retainDays) purgeDir.retainDays = 12 self.assertEqual(12, purgeDir.retainDays) def testConstructor_009(self): """ Test assignment of retainDays attribute, valid value (string representing integer). """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.retainDays) purgeDir.retainDays = "12" self.assertEqual(12, purgeDir.retainDays) def testConstructor_010(self): """ Test assignment of retainDays attribute, invalid value (empty string). """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.retainDays) self.failUnlessAssignRaises(ValueError, purgeDir, "retainDays", "") self.assertEqual(None, purgeDir.retainDays) def testConstructor_011(self): """ Test assignment of retainDays attribute, invalid value (non-integer, like a list). """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.retainDays) self.failUnlessAssignRaises(ValueError, purgeDir, "retainDays", []) self.assertEqual(None, purgeDir.retainDays) def testConstructor_012(self): """ Test assignment of retainDays attribute, invalid value (string representing non-integer). """ purgeDir = PurgeDir() self.assertEqual(None, purgeDir.retainDays) self.failUnlessAssignRaises(ValueError, purgeDir, "retainDays", "blech") self.assertEqual(None, purgeDir.retainDays) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ purgeDir1 = PurgeDir() purgeDir2 = PurgeDir() self.assertEqual(purgeDir1, purgeDir2) self.assertTrue(purgeDir1 == purgeDir2) self.assertTrue(not purgeDir1 < purgeDir2) self.assertTrue(purgeDir1 <= purgeDir2) self.assertTrue(not purgeDir1 > purgeDir2) self.assertTrue(purgeDir1 >= purgeDir2) self.assertTrue(not purgeDir1 != purgeDir2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ purgeDir1 = PurgeDir("/etc/whatever", 12) purgeDir2 = PurgeDir("/etc/whatever", 12) self.assertTrue(purgeDir1 == purgeDir2) self.assertTrue(not purgeDir1 < purgeDir2) self.assertTrue(purgeDir1 <= purgeDir2) self.assertTrue(not purgeDir1 > purgeDir2) self.assertTrue(purgeDir1 >= purgeDir2) self.assertTrue(not purgeDir1 != purgeDir2) def testComparison_003(self): """ Test comparison of two differing objects, absolutePath differs (one None). """ purgeDir1 = PurgeDir() purgeDir2 = PurgeDir(absolutePath="/whatever") self.assertNotEqual(purgeDir1, purgeDir2) self.assertTrue(not purgeDir1 == purgeDir2) self.assertTrue(purgeDir1 < purgeDir2) self.assertTrue(purgeDir1 <= purgeDir2) self.assertTrue(not purgeDir1 > purgeDir2) self.assertTrue(not purgeDir1 >= purgeDir2) self.assertTrue(purgeDir1 != purgeDir2) def testComparison_004(self): """ Test comparison of two differing objects, absolutePath differs. """ purgeDir1 = PurgeDir("/etc/blech", 12) purgeDir2 = PurgeDir("/etc/whatever", 12) self.assertNotEqual(purgeDir1, purgeDir2) self.assertTrue(not purgeDir1 == purgeDir2) self.assertTrue(purgeDir1 < purgeDir2) self.assertTrue(purgeDir1 <= purgeDir2) self.assertTrue(not purgeDir1 > purgeDir2) self.assertTrue(not purgeDir1 >= purgeDir2) self.assertTrue(purgeDir1 != purgeDir2) def testComparison_005(self): """ Test comparison of two differing objects, retainDays differs (one None). """ purgeDir1 = PurgeDir() purgeDir2 = PurgeDir(retainDays=365) self.assertNotEqual(purgeDir1, purgeDir2) self.assertTrue(not purgeDir1 == purgeDir2) self.assertTrue(purgeDir1 < purgeDir2) self.assertTrue(purgeDir1 <= purgeDir2) self.assertTrue(not purgeDir1 > purgeDir2) self.assertTrue(not purgeDir1 >= purgeDir2) self.assertTrue(purgeDir1 != purgeDir2) def testComparison_006(self): """ Test comparison of two differing objects, retainDays differs. """ purgeDir1 = PurgeDir("/etc/whatever", 365) purgeDir2 = PurgeDir("/etc/whatever", 12) self.assertNotEqual(purgeDir1, purgeDir2) self.assertTrue(not purgeDir1 == purgeDir2) self.assertTrue(not purgeDir1 < purgeDir2) self.assertTrue(not purgeDir1 <= purgeDir2) self.assertTrue(purgeDir1 > purgeDir2) self.assertTrue(purgeDir1 >= purgeDir2) self.assertTrue(purgeDir1 != purgeDir2) ###################### # TestLocalPeer class ###################### class TestLocalPeer(unittest.TestCase): """Tests for the LocalPeer class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalPeer() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ localPeer = LocalPeer() self.assertEqual(None, localPeer.name) self.assertEqual(None, localPeer.collectDir) self.assertEqual(None, localPeer.ignoreFailureMode) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ localPeer = LocalPeer("myname", "/whatever", "all") self.assertEqual("myname", localPeer.name) self.assertEqual("/whatever", localPeer.collectDir) self.assertEqual("all", localPeer.ignoreFailureMode) def testConstructor_003(self): """ Test assignment of name attribute, None value. """ localPeer = LocalPeer(name="myname") self.assertEqual("myname", localPeer.name) localPeer.name = None self.assertEqual(None, localPeer.name) def testConstructor_004(self): """ Test assignment of name attribute, valid value. """ localPeer = LocalPeer() self.assertEqual(None, localPeer.name) localPeer.name = "myname" self.assertEqual("myname", localPeer.name) def testConstructor_005(self): """ Test assignment of name attribute, invalid value (empty). """ localPeer = LocalPeer() self.assertEqual(None, localPeer.name) self.failUnlessAssignRaises(ValueError, localPeer, "name", "") self.assertEqual(None, localPeer.name) def testConstructor_006(self): """ Test assignment of collectDir attribute, None value. """ localPeer = LocalPeer(collectDir="/whatever") self.assertEqual("/whatever", localPeer.collectDir) localPeer.collectDir = None self.assertEqual(None, localPeer.collectDir) def testConstructor_007(self): """ Test assignment of collectDir attribute, valid value. """ localPeer = LocalPeer() self.assertEqual(None, localPeer.collectDir) localPeer.collectDir = "/etc/stuff" self.assertEqual("/etc/stuff", localPeer.collectDir) def testConstructor_008(self): """ Test assignment of collectDir attribute, invalid value (empty). """ localPeer = LocalPeer() self.assertEqual(None, localPeer.collectDir) self.failUnlessAssignRaises(ValueError, localPeer, "collectDir", "") self.assertEqual(None, localPeer.collectDir) def testConstructor_009(self): """ Test assignment of collectDir attribute, invalid value (non-absolute). """ localPeer = LocalPeer() self.assertEqual(None, localPeer.collectDir) self.failUnlessAssignRaises(ValueError, localPeer, "collectDir", "bogus") self.assertEqual(None, localPeer.collectDir) def testConstructor_010(self): """ Test assignment of ignoreFailureMode attribute, valid values. """ localPeer = LocalPeer() self.assertEqual(None, localPeer.ignoreFailureMode) localPeer.ignoreFailureMode = "none" self.assertEqual("none", localPeer.ignoreFailureMode) localPeer.ignoreFailureMode = "all" self.assertEqual("all", localPeer.ignoreFailureMode) localPeer.ignoreFailureMode = "daily" self.assertEqual("daily", localPeer.ignoreFailureMode) localPeer.ignoreFailureMode = "weekly" self.assertEqual("weekly", localPeer.ignoreFailureMode) def testConstructor_011(self): """ Test assignment of ignoreFailureMode attribute, invalid value. """ localPeer = LocalPeer() self.assertEqual(None, localPeer.ignoreFailureMode) self.failUnlessAssignRaises(ValueError, localPeer, "ignoreFailureMode", "bogus") def testConstructor_012(self): """ Test assignment of ignoreFailureMode attribute, None value. """ localPeer = LocalPeer() self.assertEqual(None, localPeer.ignoreFailureMode) localPeer.ignoreFailureMode = None self.assertEqual(None, localPeer.ignoreFailureMode) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ localPeer1 = LocalPeer() localPeer2 = LocalPeer() self.assertEqual(localPeer1, localPeer2) self.assertTrue(localPeer1 == localPeer2) self.assertTrue(not localPeer1 < localPeer2) self.assertTrue(localPeer1 <= localPeer2) self.assertTrue(not localPeer1 > localPeer2) self.assertTrue(localPeer1 >= localPeer2) self.assertTrue(not localPeer1 != localPeer2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ localPeer1 = LocalPeer("myname", "/etc/stuff", "all") localPeer2 = LocalPeer("myname", "/etc/stuff", "all") self.assertTrue(localPeer1 == localPeer2) self.assertTrue(not localPeer1 < localPeer2) self.assertTrue(localPeer1 <= localPeer2) self.assertTrue(not localPeer1 > localPeer2) self.assertTrue(localPeer1 >= localPeer2) self.assertTrue(not localPeer1 != localPeer2) def testComparison_003(self): """ Test comparison of two differing objects, name differs (one None). """ localPeer1 = LocalPeer() localPeer2 = LocalPeer(name="blech") self.assertNotEqual(localPeer1, localPeer2) self.assertTrue(not localPeer1 == localPeer2) self.assertTrue(localPeer1 < localPeer2) self.assertTrue(localPeer1 <= localPeer2) self.assertTrue(not localPeer1 > localPeer2) self.assertTrue(not localPeer1 >= localPeer2) self.assertTrue(localPeer1 != localPeer2) def testComparison_004(self): """ Test comparison of two differing objects, name differs. """ localPeer1 = LocalPeer("name", "/etc/stuff", "all") localPeer2 = LocalPeer("name", "/etc/whatever", "all") self.assertNotEqual(localPeer1, localPeer2) self.assertTrue(not localPeer1 == localPeer2) self.assertTrue(localPeer1 < localPeer2) self.assertTrue(localPeer1 <= localPeer2) self.assertTrue(not localPeer1 > localPeer2) self.assertTrue(not localPeer1 >= localPeer2) self.assertTrue(localPeer1 != localPeer2) def testComparison_005(self): """ Test comparison of two differing objects, collectDir differs (one None). """ localPeer1 = LocalPeer() localPeer2 = LocalPeer(collectDir="/etc/whatever") self.assertNotEqual(localPeer1, localPeer2) self.assertTrue(not localPeer1 == localPeer2) self.assertTrue(localPeer1 < localPeer2) self.assertTrue(localPeer1 <= localPeer2) self.assertTrue(not localPeer1 > localPeer2) self.assertTrue(not localPeer1 >= localPeer2) self.assertTrue(localPeer1 != localPeer2) def testComparison_006(self): """ Test comparison of two differing objects, collectDir differs. """ localPeer1 = LocalPeer("name2", "/etc/stuff", "all") localPeer2 = LocalPeer("name1", "/etc/stuff", "all") self.assertNotEqual(localPeer1, localPeer2) self.assertTrue(not localPeer1 == localPeer2) self.assertTrue(not localPeer1 < localPeer2) self.assertTrue(not localPeer1 <= localPeer2) self.assertTrue(localPeer1 > localPeer2) self.assertTrue(localPeer1 >= localPeer2) self.assertTrue(localPeer1 != localPeer2) def testComparison_008(self): """ Test comparison of two differing objects, ignoreFailureMode differs (one None). """ localPeer1 = LocalPeer() localPeer2 = LocalPeer(ignoreFailureMode="all") self.assertNotEqual(localPeer1, localPeer2) self.assertTrue(not localPeer1 == localPeer2) self.assertTrue(localPeer1 < localPeer2) self.assertTrue(localPeer1 <= localPeer2) self.assertTrue(not localPeer1 > localPeer2) self.assertTrue(not localPeer1 >= localPeer2) self.assertTrue(localPeer1 != localPeer2) def testComparison_009(self): """ Test comparison of two differing objects, collectDir differs. """ localPeer1 = LocalPeer("name1", "/etc/stuff", "none") localPeer2 = LocalPeer("name1", "/etc/stuff", "all") self.assertNotEqual(localPeer1, localPeer2) self.assertTrue(not localPeer1 == localPeer2) self.assertTrue(not localPeer1 < localPeer2) self.assertTrue(not localPeer1 <= localPeer2) self.assertTrue(localPeer1 > localPeer2) self.assertTrue(localPeer1 >= localPeer2) self.assertTrue(localPeer1 != localPeer2) ####################### # TestRemotePeer class ####################### class TestRemotePeer(unittest.TestCase): """Tests for the RemotePeer class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = RemotePeer() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.name) self.assertEqual(None, remotePeer.collectDir) self.assertEqual(None, remotePeer.remoteUser) self.assertEqual(None, remotePeer.rcpCommand) self.assertEqual(None, remotePeer.rshCommand) self.assertEqual(None, remotePeer.cbackCommand) self.assertEqual(False, remotePeer.managed) self.assertEqual(None, remotePeer.managedActions) self.assertEqual(None, remotePeer.ignoreFailureMode) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ remotePeer = RemotePeer("myname", "/stuff", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertEqual("myname", remotePeer.name) self.assertEqual("/stuff", remotePeer.collectDir) self.assertEqual("backup", remotePeer.remoteUser) self.assertEqual("scp -1 -B", remotePeer.rcpCommand) self.assertEqual("ssh", remotePeer.rshCommand) self.assertEqual("cback", remotePeer.cbackCommand) self.assertEqual(True, remotePeer.managed) self.assertEqual(["collect"], remotePeer.managedActions) self.assertEqual("all", remotePeer.ignoreFailureMode) def testConstructor_003(self): """ Test assignment of name attribute, None value. """ remotePeer = RemotePeer(name="myname") self.assertEqual("myname", remotePeer.name) remotePeer.name = None self.assertEqual(None, remotePeer.name) def testConstructor_004(self): """ Test assignment of name attribute, valid value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.name) remotePeer.name = "namename" self.assertEqual("namename", remotePeer.name) def testConstructor_005(self): """ Test assignment of name attribute, invalid value (empty). """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.name) self.failUnlessAssignRaises(ValueError, remotePeer, "name", "") self.assertEqual(None, remotePeer.name) def testConstructor_006(self): """ Test assignment of collectDir attribute, None value. """ remotePeer = RemotePeer(collectDir="/etc/stuff") self.assertEqual("/etc/stuff", remotePeer.collectDir) remotePeer.collectDir = None self.assertEqual(None, remotePeer.collectDir) def testConstructor_007(self): """ Test assignment of collectDir attribute, valid value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.collectDir) remotePeer.collectDir = "/tmp" self.assertEqual("/tmp", remotePeer.collectDir) def testConstructor_008(self): """ Test assignment of collectDir attribute, invalid value (empty). """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.collectDir) self.failUnlessAssignRaises(ValueError, remotePeer, "collectDir", "") self.assertEqual(None, remotePeer.collectDir) def testConstructor_009(self): """ Test assignment of collectDir attribute, invalid value (non-absolute). """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.collectDir) self.failUnlessAssignRaises(ValueError, remotePeer, "collectDir", "bogus/stuff/there") self.assertEqual(None, remotePeer.collectDir) def testConstructor_010(self): """ Test assignment of remoteUser attribute, None value. """ remotePeer = RemotePeer(remoteUser="spot") self.assertEqual("spot", remotePeer.remoteUser) remotePeer.remoteUser = None self.assertEqual(None, remotePeer.remoteUser) def testConstructor_011(self): """ Test assignment of remoteUser attribute, valid value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.remoteUser) remotePeer.remoteUser = "spot" self.assertEqual("spot", remotePeer.remoteUser) def testConstructor_012(self): """ Test assignment of remoteUser attribute, invalid value (empty). """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.remoteUser) self.failUnlessAssignRaises(ValueError, remotePeer, "remoteUser", "") self.assertEqual(None, remotePeer.remoteUser) def testConstructor_013(self): """ Test assignment of rcpCommand attribute, None value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.rcpCommand) remotePeer.rcpCommand = "scp" self.assertEqual("scp", remotePeer.rcpCommand) def testConstructor_014(self): """ Test assignment of rcpCommand attribute, valid value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.rcpCommand) remotePeer.rcpCommand = "scp" self.assertEqual("scp", remotePeer.rcpCommand) def testConstructor_015(self): """ Test assignment of rcpCommand attribute, invalid value (empty). """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.rcpCommand) self.failUnlessAssignRaises(ValueError, remotePeer, "rcpCommand", "") self.assertEqual(None, remotePeer.rcpCommand) def testConstructor_016(self): """ Test assignment of rshCommand attribute, valid value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.rshCommand) remotePeer.rshCommand = "scp" self.assertEqual("scp", remotePeer.rshCommand) def testConstructor_017(self): """ Test assignment of rshCommand attribute, invalid value (empty). """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.rshCommand) self.failUnlessAssignRaises(ValueError, remotePeer, "rshCommand", "") self.assertEqual(None, remotePeer.rshCommand) def testConstructor_018(self): """ Test assignment of cbackCommand attribute, valid value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.cbackCommand) remotePeer.cbackCommand = "scp" self.assertEqual("scp", remotePeer.cbackCommand) def testConstructor_019(self): """ Test assignment of cbackCommand attribute, invalid value (empty). """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.cbackCommand) self.failUnlessAssignRaises(ValueError, remotePeer, "cbackCommand", "") self.assertEqual(None, remotePeer.cbackCommand) def testConstructor_021(self): """ Test assignment of managed attribute, None value. """ remotePeer = RemotePeer(managed=True) self.assertEqual(True, remotePeer.managed) remotePeer.managed = None self.assertEqual(False, remotePeer.managed) def testConstructor_022(self): """ Test assignment of managed attribute, valid value (real boolean). """ remotePeer = RemotePeer() self.assertEqual(False, remotePeer.managed) remotePeer.managed = True self.assertEqual(True, remotePeer.managed) remotePeer.managed = False self.assertEqual(False, remotePeer.managed) def testConstructor_023(self): """ Test assignment of managed attribute, valid value (expression). """ remotePeer = RemotePeer() self.assertEqual(False, remotePeer.managed) remotePeer.managed = 0 self.assertEqual(False, remotePeer.managed) remotePeer.managed = [] self.assertEqual(False, remotePeer.managed) remotePeer.managed = None self.assertEqual(False, remotePeer.managed) remotePeer.managed = ["a"] self.assertEqual(True, remotePeer.managed) remotePeer.managed = 3 self.assertEqual(True, remotePeer.managed) def testConstructor_024(self): """ Test assignment of managedActions attribute, None value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.managedActions) remotePeer.managedActions = None self.assertEqual(None, remotePeer.managedActions) def testConstructor_025(self): """ Test assignment of managedActions attribute, empty list. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.managedActions) remotePeer.managedActions = [] self.assertEqual([], remotePeer.managedActions) def testConstructor_026(self): """ Test assignment of managedActions attribute, non-empty list, valid values. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.managedActions) remotePeer.managedActions = [ "a", "b", ] self.assertEqual(["a", "b"], remotePeer.managedActions) def testConstructor_027(self): """ Test assignment of managedActions attribute, non-empty list, invalid value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.managedActions) self.failUnlessAssignRaises(ValueError, remotePeer, "managedActions", ["KEN"]) self.assertEqual(None, remotePeer.managedActions) self.failUnlessAssignRaises(ValueError, remotePeer, "managedActions", ["hello, world"]) self.assertEqual(None, remotePeer.managedActions) self.failUnlessAssignRaises(ValueError, remotePeer, "managedActions", ["dash-word"]) self.assertEqual(None, remotePeer.managedActions) self.failUnlessAssignRaises(ValueError, remotePeer, "managedActions", [""]) self.assertEqual(None, remotePeer.managedActions) self.failUnlessAssignRaises(ValueError, remotePeer, "managedActions", [None]) self.assertEqual(None, remotePeer.managedActions) def testConstructor_028(self): """ Test assignment of managedActions attribute, non-empty list, mixed values. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.managedActions) self.failUnlessAssignRaises(ValueError, remotePeer, "managedActions", ["ken", "dash-word"]) def testConstructor_029(self): """ Test assignment of ignoreFailureMode attribute, valid values. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.ignoreFailureMode) remotePeer.ignoreFailureMode = "none" self.assertEqual("none", remotePeer.ignoreFailureMode) remotePeer.ignoreFailureMode = "all" self.assertEqual("all", remotePeer.ignoreFailureMode) remotePeer.ignoreFailureMode = "daily" self.assertEqual("daily", remotePeer.ignoreFailureMode) remotePeer.ignoreFailureMode = "weekly" self.assertEqual("weekly", remotePeer.ignoreFailureMode) def testConstructor_030(self): """ Test assignment of ignoreFailureMode attribute, invalid value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.ignoreFailureMode) self.failUnlessAssignRaises(ValueError, remotePeer, "ignoreFailureMode", "bogus") def testConstructor_031(self): """ Test assignment of ignoreFailureMode attribute, None value. """ remotePeer = RemotePeer() self.assertEqual(None, remotePeer.ignoreFailureMode) remotePeer.ignoreFailureMode = None self.assertEqual(None, remotePeer.ignoreFailureMode) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer() self.assertEqual(remotePeer1, remotePeer2) self.assertTrue(remotePeer1 == remotePeer2) self.assertTrue(not remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(remotePeer1 >= remotePeer2) self.assertTrue(not remotePeer1 != remotePeer2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertTrue(remotePeer1 == remotePeer2) self.assertTrue(not remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(remotePeer1 >= remotePeer2) self.assertTrue(not remotePeer1 != remotePeer2) def testComparison_003(self): """ Test comparison of two differing objects, name differs (one None). """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer(name="name") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_004(self): """ Test comparison of two differing objects, name differs. """ remotePeer1 = RemotePeer("name1", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") remotePeer2 = RemotePeer("name2", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_005(self): """ Test comparison of two differing objects, collectDir differs (one None). """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer(collectDir="/tmp") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_006(self): """ Test comparison of two differing objects, collectDir differs. """ remotePeer1 = RemotePeer("name", "/etc", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_007(self): """ Test comparison of two differing objects, remoteUser differs (one None). """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer(remoteUser="spot") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_008(self): """ Test comparison of two differing objects, remoteUser differs. """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "spot", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(not remotePeer1 < remotePeer2) self.assertTrue(not remotePeer1 <= remotePeer2) self.assertTrue(remotePeer1 > remotePeer2) self.assertTrue(remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_009(self): """ Test comparison of two differing objects, rcpCommand differs (one None). """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer(rcpCommand="scp") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_010(self): """ Test comparison of two differing objects, rcpCommand differs. """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -2 -B", "ssh", "cback", True, ["collect"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(not remotePeer1 < remotePeer2) self.assertTrue(not remotePeer1 <= remotePeer2) self.assertTrue(remotePeer1 > remotePeer2) self.assertTrue(remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_011(self): """ Test comparison of two differing objects, rshCommand differs (one None). """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer(rshCommand="ssh") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_012(self): """ Test comparison of two differing objects, rshCommand differs. """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh2", "cback", True, ["collect"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh1", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(not remotePeer1 < remotePeer2) self.assertTrue(not remotePeer1 <= remotePeer2) self.assertTrue(remotePeer1 > remotePeer2) self.assertTrue(remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_013(self): """ Test comparison of two differing objects, cbackCommand differs (one None). """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer(cbackCommand="cback") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_014(self): """ Test comparison of two differing objects, cbackCommand differs. """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback2", True, ["collect"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback1", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(not remotePeer1 < remotePeer2) self.assertTrue(not remotePeer1 <= remotePeer2) self.assertTrue(remotePeer1 > remotePeer2) self.assertTrue(remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_015(self): """ Test comparison of two differing objects, managed differs (one None). """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer(managed=True) self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_016(self): """ Test comparison of two differing objects, managed differs. """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", False, ["collect"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_017(self): """ Test comparison of two differing objects, managedActions differs (one None, one empty). """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, None, "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, [], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_018(self): """ Test comparison of two differing objects, managedActions differs (one None, one not empty). """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, None, "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_019(self): """ Test comparison of two differing objects, managedActions differs (one empty, one not empty). """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, [], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_020(self): """ Test comparison of two differing objects, managedActions differs (both not empty). """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["purge"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(not remotePeer1 < remotePeer2) self.assertTrue(not remotePeer1 <= remotePeer2) self.assertTrue(remotePeer1 > remotePeer2) self.assertTrue(remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_021(self): """ Test comparison of two differing objects, ignoreFailureMode differs (one None). """ remotePeer1 = RemotePeer() remotePeer2 = RemotePeer(ignoreFailureMode="all") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) def testComparison_022(self): """ Test comparison of two differing objects, ignoreFailureMode differs. """ remotePeer1 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "all") remotePeer2 = RemotePeer("name", "/etc/stuff/tmp/X11", "backup", "scp -1 -B", "ssh", "cback", True, ["collect"], "none") self.assertNotEqual(remotePeer1, remotePeer2) self.assertTrue(not remotePeer1 == remotePeer2) self.assertTrue(remotePeer1 < remotePeer2) self.assertTrue(remotePeer1 <= remotePeer2) self.assertTrue(not remotePeer1 > remotePeer2) self.assertTrue(not remotePeer1 >= remotePeer2) self.assertTrue(remotePeer1 != remotePeer2) ############################ # TestReferenceConfig class ############################ class TestReferenceConfig(unittest.TestCase): """Tests for the ReferenceConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = ReferenceConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ reference = ReferenceConfig() self.assertEqual(None, reference.author) self.assertEqual(None, reference.revision) self.assertEqual(None, reference.description) self.assertEqual(None, reference.generator) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ reference = ReferenceConfig("one", "two", "three", "four") self.assertEqual("one", reference.author) self.assertEqual("two", reference.revision) self.assertEqual("three", reference.description) self.assertEqual("four", reference.generator) def testConstructor_003(self): """ Test assignment of author attribute, None value. """ reference = ReferenceConfig(author="one") self.assertEqual("one", reference.author) reference.author = None self.assertEqual(None, reference.author) def testConstructor_004(self): """ Test assignment of author attribute, valid value. """ reference = ReferenceConfig() self.assertEqual(None, reference.author) reference.author = "one" self.assertEqual("one", reference.author) def testConstructor_005(self): """ Test assignment of author attribute, valid value (empty). """ reference = ReferenceConfig() self.assertEqual(None, reference.author) reference.author = "" self.assertEqual("", reference.author) def testConstructor_006(self): """ Test assignment of revision attribute, None value. """ reference = ReferenceConfig(revision="one") self.assertEqual("one", reference.revision) reference.revision = None self.assertEqual(None, reference.revision) def testConstructor_007(self): """ Test assignment of revision attribute, valid value. """ reference = ReferenceConfig() self.assertEqual(None, reference.revision) reference.revision = "one" self.assertEqual("one", reference.revision) def testConstructor_008(self): """ Test assignment of revision attribute, valid value (empty). """ reference = ReferenceConfig() self.assertEqual(None, reference.revision) reference.revision = "" self.assertEqual("", reference.revision) def testConstructor_009(self): """ Test assignment of description attribute, None value. """ reference = ReferenceConfig(description="one") self.assertEqual("one", reference.description) reference.description = None self.assertEqual(None, reference.description) def testConstructor_010(self): """ Test assignment of description attribute, valid value. """ reference = ReferenceConfig() self.assertEqual(None, reference.description) reference.description = "one" self.assertEqual("one", reference.description) def testConstructor_011(self): """ Test assignment of description attribute, valid value (empty). """ reference = ReferenceConfig() self.assertEqual(None, reference.description) reference.description = "" self.assertEqual("", reference.description) def testConstructor_012(self): """ Test assignment of generator attribute, None value. """ reference = ReferenceConfig(generator="one") self.assertEqual("one", reference.generator) reference.generator = None self.assertEqual(None, reference.generator) def testConstructor_013(self): """ Test assignment of generator attribute, valid value. """ reference = ReferenceConfig() self.assertEqual(None, reference.generator) reference.generator = "one" self.assertEqual("one", reference.generator) def testConstructor_014(self): """ Test assignment of generator attribute, valid value (empty). """ reference = ReferenceConfig() self.assertEqual(None, reference.generator) reference.generator = "" self.assertEqual("", reference.generator) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ reference1 = ReferenceConfig() reference2 = ReferenceConfig() self.assertEqual(reference1, reference2) self.assertTrue(reference1 == reference2) self.assertTrue(not reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(reference1 >= reference2) self.assertTrue(not reference1 != reference2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ reference1 = ReferenceConfig("one", "two", "three", "four") reference2 = ReferenceConfig("one", "two", "three", "four") self.assertTrue(reference1 == reference2) self.assertTrue(not reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(reference1 >= reference2) self.assertTrue(not reference1 != reference2) def testComparison_003(self): """ Test comparison of two differing objects, author differs (one None). """ reference1 = ReferenceConfig() reference2 = ReferenceConfig(author="one") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(not reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_004(self): """ Test comparison of two differing objects, author differs (one empty). """ reference1 = ReferenceConfig("", "two", "three", "four") reference2 = ReferenceConfig("one", "two", "three", "four") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(not reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_005(self): """ Test comparison of two differing objects, author differs. """ reference1 = ReferenceConfig("one", "two", "three", "four") reference2 = ReferenceConfig("author", "two", "three", "four") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(not reference1 < reference2) self.assertTrue(not reference1 <= reference2) self.assertTrue(reference1 > reference2) self.assertTrue(reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_006(self): """ Test comparison of two differing objects, revision differs (one None). """ reference1 = ReferenceConfig() reference2 = ReferenceConfig(revision="one") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(not reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_007(self): """ Test comparison of two differing objects, revision differs (one empty). """ reference1 = ReferenceConfig("one", "two", "three", "four") reference2 = ReferenceConfig("one", "", "three", "four") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(not reference1 < reference2) self.assertTrue(not reference1 <= reference2) self.assertTrue(reference1 > reference2) self.assertTrue(reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_008(self): """ Test comparison of two differing objects, revision differs. """ reference1 = ReferenceConfig("one", "two", "three", "four") reference2 = ReferenceConfig("one", "revision", "three", "four") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(not reference1 < reference2) self.assertTrue(not reference1 <= reference2) self.assertTrue(reference1 > reference2) self.assertTrue(reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_009(self): """ Test comparison of two differing objects, description differs (one None). """ reference1 = ReferenceConfig() reference2 = ReferenceConfig(description="one") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(not reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_010(self): """ Test comparison of two differing objects, description differs (one empty). """ reference1 = ReferenceConfig("one", "two", "three", "four") reference2 = ReferenceConfig("one", "two", "", "four") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(not reference1 < reference2) self.assertTrue(not reference1 <= reference2) self.assertTrue(reference1 > reference2) self.assertTrue(reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_011(self): """ Test comparison of two differing objects, description differs. """ reference1 = ReferenceConfig("one", "two", "description", "four") reference2 = ReferenceConfig("one", "two", "three", "four") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(not reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_012(self): """ Test comparison of two differing objects, generator differs (one None). """ reference1 = ReferenceConfig() reference2 = ReferenceConfig(generator="one") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(not reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_013(self): """ Test comparison of two differing objects, generator differs (one empty). """ reference1 = ReferenceConfig("one", "two", "three", "") reference2 = ReferenceConfig("one", "two", "three", "four") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(not reference1 >= reference2) self.assertTrue(reference1 != reference2) def testComparison_014(self): """ Test comparison of two differing objects, generator differs. """ reference1 = ReferenceConfig("one", "two", "three", "four") reference2 = ReferenceConfig("one", "two", "three", "generator") self.assertNotEqual(reference1, reference2) self.assertTrue(not reference1 == reference2) self.assertTrue(reference1 < reference2) self.assertTrue(reference1 <= reference2) self.assertTrue(not reference1 > reference2) self.assertTrue(not reference1 >= reference2) self.assertTrue(reference1 != reference2) ############################# # TestExtensionsConfig class ############################# class TestExtensionsConfig(unittest.TestCase): """Tests for the ExtensionsConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = ExtensionsConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ extensions = ExtensionsConfig() self.assertEqual(None, extensions.orderMode) self.assertEqual(None, extensions.actions) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values (empty list), positional arguments. """ extensions = ExtensionsConfig([], None) self.assertEqual(None, extensions.orderMode) self.assertEqual([], extensions.actions) extensions = ExtensionsConfig([], "index") self.assertEqual("index", extensions.orderMode) self.assertEqual([], extensions.actions) extensions = ExtensionsConfig([], "dependency") self.assertEqual("dependency", extensions.orderMode) self.assertEqual([], extensions.actions) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values (non-empty list), named arguments. """ extensions = ExtensionsConfig(orderMode=None, actions=[ExtendedAction()]) self.assertEqual(None, extensions.orderMode) self.assertEqual([ExtendedAction()], extensions.actions) extensions = ExtensionsConfig(orderMode="index", actions=[ExtendedAction()]) self.assertEqual("index", extensions.orderMode) self.assertEqual([ExtendedAction()], extensions.actions) extensions = ExtensionsConfig(orderMode="dependency", actions=[ExtendedAction()]) self.assertEqual("dependency", extensions.orderMode) self.assertEqual([ExtendedAction()], extensions.actions) def testConstructor_004(self): """ Test assignment of actions attribute, None value. """ extensions = ExtensionsConfig([]) self.assertEqual(None, extensions.orderMode) self.assertEqual([], extensions.actions) extensions.actions = None self.assertEqual(None, extensions.actions) def testConstructor_005(self): """ Test assignment of actions attribute, [] value. """ extensions = ExtensionsConfig() self.assertEqual(None, extensions.orderMode) self.assertEqual(None, extensions.actions) extensions.actions = [] self.assertEqual([], extensions.actions) def testConstructor_006(self): """ Test assignment of actions attribute, single valid entry. """ extensions = ExtensionsConfig() self.assertEqual(None, extensions.orderMode) self.assertEqual(None, extensions.actions) extensions.actions = [ ExtendedAction(), ] self.assertEqual([ExtendedAction()], extensions.actions) def testConstructor_007(self): """ Test assignment of actions attribute, multiple valid entries. """ extensions = ExtensionsConfig() self.assertEqual(None, extensions.orderMode) self.assertEqual(None, extensions.actions) extensions.actions = [ ExtendedAction("a", "b", "c", 1), ExtendedAction("d", "e", "f", 2), ] self.assertEqual([ExtendedAction("a", "b", "c", 1), ExtendedAction("d", "e", "f", 2)], extensions.actions) def testConstructor_009(self): """ Test assignment of actions attribute, single invalid entry (not an ExtendedAction). """ extensions = ExtensionsConfig() self.assertEqual(None, extensions.orderMode) self.assertEqual(None, extensions.actions) self.failUnlessAssignRaises(ValueError, extensions, "actions", [RemotePeer()]) self.assertEqual(None, extensions.actions) def testConstructor_010(self): """ Test assignment of actions attribute, mixed valid and invalid entries. """ extensions = ExtensionsConfig() self.assertEqual(None, extensions.orderMode) self.assertEqual(None, extensions.actions) self.failUnlessAssignRaises(ValueError, extensions, "actions", [ExtendedAction(), RemotePeer()]) self.assertEqual(None, extensions.actions) def testConstructor_011(self): """ Test assignment of orderMode attribute, None value. """ extensions = ExtensionsConfig(orderMode="index") self.assertEqual("index", extensions.orderMode) self.assertEqual(None, extensions.actions) extensions.orderMode = None self.assertEqual(None, extensions.orderMode) def testConstructor_012(self): """ Test assignment of orderMode attribute, valid values. """ extensions = ExtensionsConfig() self.assertEqual(None, extensions.orderMode) self.assertEqual(None, extensions.actions) extensions.orderMode = "index" self.assertEqual("index", extensions.orderMode) extensions.orderMode = "dependency" self.assertEqual("dependency", extensions.orderMode) def testConstructor_013(self): """ Test assignment of orderMode attribute, invalid values. """ extensions = ExtensionsConfig() self.assertEqual(None, extensions.orderMode) self.assertEqual(None, extensions.actions) self.failUnlessAssignRaises(ValueError, extensions, "orderMode", "") self.failUnlessAssignRaises(ValueError, extensions, "orderMode", "bogus") self.failUnlessAssignRaises(ValueError, extensions, "orderMode", "indexes") self.failUnlessAssignRaises(ValueError, extensions, "orderMode", "indices") self.failUnlessAssignRaises(ValueError, extensions, "orderMode", "dependencies") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ extensions1 = ExtensionsConfig() extensions2 = ExtensionsConfig() self.assertEqual(extensions1, extensions2) self.assertTrue(extensions1 == extensions2) self.assertTrue(not extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(extensions1 >= extensions2) self.assertTrue(not extensions1 != extensions2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None (empty lists). """ extensions1 = ExtensionsConfig([], "index") extensions2 = ExtensionsConfig([], "index") self.assertEqual(extensions1, extensions2) self.assertTrue(extensions1 == extensions2) self.assertTrue(not extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(extensions1 >= extensions2) self.assertTrue(not extensions1 != extensions2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None (non-empty lists). """ extensions1 = ExtensionsConfig([ExtendedAction()], "index") extensions2 = ExtensionsConfig([ExtendedAction()], "index") self.assertEqual(extensions1, extensions2) self.assertTrue(extensions1 == extensions2) self.assertTrue(not extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(extensions1 >= extensions2) self.assertTrue(not extensions1 != extensions2) def testComparison_004(self): """ Test comparison of two differing objects, actions differs (one None, one empty). """ extensions1 = ExtensionsConfig(None) extensions2 = ExtensionsConfig([]) self.assertNotEqual(extensions1, extensions2) self.assertTrue(not extensions1 == extensions2) self.assertTrue(extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(not extensions1 >= extensions2) self.assertTrue(extensions1 != extensions2) def testComparison_005(self): """ Test comparison of two differing objects, actions differs (one None, one not empty). """ extensions1 = ExtensionsConfig(None) extensions2 = ExtensionsConfig([ExtendedAction()]) self.assertNotEqual(extensions1, extensions2) self.assertTrue(not extensions1 == extensions2) self.assertTrue(extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(not extensions1 >= extensions2) self.assertTrue(extensions1 != extensions2) def testComparison_006(self): """ Test comparison of two differing objects, actions differs (one empty, one not empty). """ extensions1 = ExtensionsConfig([]) extensions2 = ExtensionsConfig([ExtendedAction()]) self.assertNotEqual(extensions1, extensions2) self.assertTrue(not extensions1 == extensions2) self.assertTrue(extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(not extensions1 >= extensions2) self.assertTrue(extensions1 != extensions2) def testComparison_007(self): """ Test comparison of two differing objects, actions differs (both not empty). """ extensions1 = ExtensionsConfig([ExtendedAction(name="one")]) extensions2 = ExtensionsConfig([ExtendedAction(name="two")]) self.assertNotEqual(extensions1, extensions2) self.assertTrue(not extensions1 == extensions2) self.assertTrue(extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(not extensions1 >= extensions2) self.assertTrue(extensions1 != extensions2) def testComparison_008(self): """ Test comparison of differing objects, orderMode differs (one None). """ extensions1 = ExtensionsConfig([], None) extensions2 = ExtensionsConfig([], "index") self.assertNotEqual(extensions1, extensions2) self.assertTrue(not extensions1 == extensions2) self.assertTrue(extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(not extensions1 >= extensions2) self.assertTrue(extensions1 != extensions2) def testComparison_009(self): """ Test comparison of differing objects, orderMode differs. """ extensions1 = ExtensionsConfig([], "dependency") extensions2 = ExtensionsConfig([], "index") self.assertNotEqual(extensions1, extensions2) self.assertTrue(not extensions1 == extensions2) self.assertTrue(extensions1 < extensions2) self.assertTrue(extensions1 <= extensions2) self.assertTrue(not extensions1 > extensions2) self.assertTrue(not extensions1 >= extensions2) self.assertTrue(extensions1 != extensions2) ########################## # TestOptionsConfig class ########################## class TestOptionsConfig(unittest.TestCase): """Tests for the OptionsConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = OptionsConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ options = OptionsConfig() self.assertEqual(None, options.startingDay) self.assertEqual(None, options.workingDir) self.assertEqual(None, options.backupUser) self.assertEqual(None, options.backupGroup) self.assertEqual(None, options.rcpCommand) self.assertEqual(None, options.rshCommand) self.assertEqual(None, options.cbackCommand) self.assertEqual(None, options.overrides) self.assertEqual(None, options.hooks) self.assertEqual(None, options.managedActions) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values (lists empty). """ options = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", [], [], "ssh", "cback", []) self.assertEqual("monday", options.startingDay) self.assertEqual("/tmp", options.workingDir) self.assertEqual("user", options.backupUser) self.assertEqual("group", options.backupGroup) self.assertEqual("scp -1 -B", options.rcpCommand) self.assertEqual("ssh", options.rshCommand) self.assertEqual("cback", options.cbackCommand) self.assertEqual([], options.overrides) self.assertEqual([], options.hooks) self.assertEqual([], options.managedActions) def testConstructor_003(self): """ Test assignment of startingDay attribute, None value. """ options = OptionsConfig(startingDay="monday") self.assertEqual("monday", options.startingDay) options.startingDay = None self.assertEqual(None, options.startingDay) def testConstructor_004(self): """ Test assignment of startingDay attribute, valid value. """ options = OptionsConfig() self.assertEqual(None, options.startingDay) options.startingDay = "monday" self.assertEqual("monday", options.startingDay) options.startingDay = "tuesday" self.assertEqual("tuesday", options.startingDay) options.startingDay = "wednesday" self.assertEqual("wednesday", options.startingDay) options.startingDay = "thursday" self.assertEqual("thursday", options.startingDay) options.startingDay = "friday" self.assertEqual("friday", options.startingDay) options.startingDay = "saturday" self.assertEqual("saturday", options.startingDay) options.startingDay = "sunday" self.assertEqual("sunday", options.startingDay) def testConstructor_005(self): """ Test assignment of startingDay attribute, invalid value (empty). """ options = OptionsConfig() self.assertEqual(None, options.startingDay) self.failUnlessAssignRaises(ValueError, options, "startingDay", "") self.assertEqual(None, options.startingDay) def testConstructor_006(self): """ Test assignment of startingDay attribute, invalid value (not in list). """ options = OptionsConfig() self.assertEqual(None, options.startingDay) self.failUnlessAssignRaises(ValueError, options, "startingDay", "dienstag") # ha, ha, pretend I'm German self.assertEqual(None, options.startingDay) def testConstructor_007(self): """ Test assignment of workingDir attribute, None value. """ options = OptionsConfig(workingDir="/tmp") self.assertEqual("/tmp", options.workingDir) options.workingDir = None self.assertEqual(None, options.workingDir) def testConstructor_008(self): """ Test assignment of workingDir attribute, valid value. """ options = OptionsConfig() self.assertEqual(None, options.workingDir) options.workingDir = "/tmp" self.assertEqual("/tmp", options.workingDir) def testConstructor_009(self): """ Test assignment of workingDir attribute, invalid value (empty). """ options = OptionsConfig() self.assertEqual(None, options.workingDir) self.failUnlessAssignRaises(ValueError, options, "workingDir", "") self.assertEqual(None, options.workingDir) def testConstructor_010(self): """ Test assignment of workingDir attribute, invalid value (non-absolute). """ options = OptionsConfig() self.assertEqual(None, options.workingDir) self.failUnlessAssignRaises(ValueError, options, "workingDir", "stuff") self.assertEqual(None, options.workingDir) def testConstructor_011(self): """ Test assignment of backupUser attribute, None value. """ options = OptionsConfig(backupUser="user") self.assertEqual("user", options.backupUser) options.backupUser = None self.assertEqual(None, options.backupUser) def testConstructor_012(self): """ Test assignment of backupUser attribute, valid value. """ options = OptionsConfig() self.assertEqual(None, options.backupUser) options.backupUser = "user" self.assertEqual("user", options.backupUser) def testConstructor_013(self): """ Test assignment of backupUser attribute, invalid value (empty). """ options = OptionsConfig() self.assertEqual(None, options.backupUser) self.failUnlessAssignRaises(ValueError, options, "backupUser", "") self.assertEqual(None, options.backupUser) def testConstructor_014(self): """ Test assignment of backupGroup attribute, None value. """ options = OptionsConfig(backupGroup="group") self.assertEqual("group", options.backupGroup) options.backupGroup = None self.assertEqual(None, options.backupGroup) def testConstructor_015(self): """ Test assignment of backupGroup attribute, valid value. """ options = OptionsConfig() self.assertEqual(None, options.backupGroup) options.backupGroup = "group" self.assertEqual("group", options.backupGroup) def testConstructor_016(self): """ Test assignment of backupGroup attribute, invalid value (empty). """ options = OptionsConfig() self.assertEqual(None, options.backupGroup) self.failUnlessAssignRaises(ValueError, options, "backupGroup", "") self.assertEqual(None, options.backupGroup) def testConstructor_017(self): """ Test assignment of rcpCommand attribute, None value. """ options = OptionsConfig(rcpCommand="command") self.assertEqual("command", options.rcpCommand) options.rcpCommand = None self.assertEqual(None, options.rcpCommand) def testConstructor_018(self): """ Test assignment of rcpCommand attribute, valid value. """ options = OptionsConfig() self.assertEqual(None, options.rcpCommand) options.rcpCommand = "command" self.assertEqual("command", options.rcpCommand) def testConstructor_019(self): """ Test assignment of rcpCommand attribute, invalid value (empty). """ options = OptionsConfig() self.assertEqual(None, options.rcpCommand) self.failUnlessAssignRaises(ValueError, options, "rcpCommand", "") self.assertEqual(None, options.rcpCommand) def testConstructor_020(self): """ Test constructor with all values filled in, with valid values (lists not empty). """ overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), ] hooks = [ PreActionHook("collect", "ls -l"), ] managedActions = [ "collect", "purge", ] options = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) self.assertEqual("monday", options.startingDay) self.assertEqual("/tmp", options.workingDir) self.assertEqual("user", options.backupUser) self.assertEqual("group", options.backupGroup) self.assertEqual("scp -1 -B", options.rcpCommand) self.assertEqual("ssh", options.rshCommand) self.assertEqual("cback", options.cbackCommand) self.assertEqual(overrides, options.overrides) self.assertEqual(hooks, options.hooks) self.assertEqual(managedActions, options.managedActions) def testConstructor_021(self): """ Test assignment of overrides attribute, None value. """ collect = OptionsConfig(overrides=[]) self.assertEqual([], collect.overrides) collect.overrides = None self.assertEqual(None, collect.overrides) def testConstructor_022(self): """ Test assignment of overrides attribute, [] value. """ collect = OptionsConfig() self.assertEqual(None, collect.overrides) collect.overrides = [] self.assertEqual([], collect.overrides) def testConstructor_023(self): """ Test assignment of overrides attribute, single valid entry. """ collect = OptionsConfig() self.assertEqual(None, collect.overrides) collect.overrides = [ CommandOverride("one", "/one"), ] self.assertEqual([CommandOverride("one", "/one")], collect.overrides) def testConstructor_024(self): """ Test assignment of overrides attribute, multiple valid entries. """ collect = OptionsConfig() self.assertEqual(None, collect.overrides) collect.overrides = [ CommandOverride("one", "/one"), CommandOverride("two", "/two"), ] self.assertEqual([CommandOverride("one", "/one"), CommandOverride("two", "/two")], collect.overrides) def testConstructor_025(self): """ Test assignment of overrides attribute, single invalid entry (None). """ collect = OptionsConfig() self.assertEqual(None, collect.overrides) self.failUnlessAssignRaises(ValueError, collect, "overrides", [None]) self.assertEqual(None, collect.overrides) def testConstructor_026(self): """ Test assignment of overrides attribute, single invalid entry (not a CommandOverride). """ collect = OptionsConfig() self.assertEqual(None, collect.overrides) self.failUnlessAssignRaises(ValueError, collect, "overrides", ["hello"]) self.assertEqual(None, collect.overrides) def testConstructor_027(self): """ Test assignment of overrides attribute, mixed valid and invalid entries. """ collect = OptionsConfig() self.assertEqual(None, collect.overrides) self.failUnlessAssignRaises(ValueError, collect, "overrides", ["hello", CommandOverride("one", "/one")]) self.assertEqual(None, collect.overrides) def testConstructor_028(self): """ Test assignment of hooks attribute, None value. """ collect = OptionsConfig(hooks=[]) self.assertEqual([], collect.hooks) collect.hooks = None self.assertEqual(None, collect.hooks) def testConstructor_029(self): """ Test assignment of hooks attribute, [] value. """ collect = OptionsConfig() self.assertEqual(None, collect.hooks) collect.hooks = [] self.assertEqual([], collect.hooks) def testConstructor_030(self): """ Test assignment of hooks attribute, single valid entry. """ collect = OptionsConfig() self.assertEqual(None, collect.hooks) collect.hooks = [ PreActionHook("stage", "df -k"), ] self.assertEqual([PreActionHook("stage", "df -k")], collect.hooks) def testConstructor_031(self): """ Test assignment of hooks attribute, multiple valid entries. """ collect = OptionsConfig() self.assertEqual(None, collect.hooks) collect.hooks = [ PreActionHook("stage", "df -k"), PostActionHook("collect", "ls -l"), ] self.assertEqual([PreActionHook("stage", "df -k"), PostActionHook("collect", "ls -l")], collect.hooks) def testConstructor_032(self): """ Test assignment of hooks attribute, single invalid entry (None). """ collect = OptionsConfig() self.assertEqual(None, collect.hooks) self.failUnlessAssignRaises(ValueError, collect, "hooks", [None]) self.assertEqual(None, collect.hooks) def testConstructor_033(self): """ Test assignment of hooks attribute, single invalid entry (not a ActionHook). """ collect = OptionsConfig() self.assertEqual(None, collect.hooks) self.failUnlessAssignRaises(ValueError, collect, "hooks", ["hello"]) self.assertEqual(None, collect.hooks) def testConstructor_034(self): """ Test assignment of hooks attribute, mixed valid and invalid entries. """ collect = OptionsConfig() self.assertEqual(None, collect.hooks) self.failUnlessAssignRaises(ValueError, collect, "hooks", ["hello", PreActionHook("stage", "df -k")]) self.assertEqual(None, collect.hooks) def testConstructor_035(self): """ Test assignment of rshCommand attribute, None value. """ options = OptionsConfig(rshCommand="command") self.assertEqual("command", options.rshCommand) options.rshCommand = None self.assertEqual(None, options.rshCommand) def testConstructor_036(self): """ Test assignment of rshCommand attribute, valid value. """ options = OptionsConfig() self.assertEqual(None, options.rshCommand) options.rshCommand = "command" self.assertEqual("command", options.rshCommand) def testConstructor_037(self): """ Test assignment of rshCommand attribute, invalid value (empty). """ options = OptionsConfig() self.assertEqual(None, options.rshCommand) self.failUnlessAssignRaises(ValueError, options, "rshCommand", "") self.assertEqual(None, options.rshCommand) def testConstructor_038(self): """ Test assignment of cbackCommand attribute, None value. """ options = OptionsConfig(cbackCommand="command") self.assertEqual("command", options.cbackCommand) options.cbackCommand = None self.assertEqual(None, options.cbackCommand) def testConstructor_039(self): """ Test assignment of cbackCommand attribute, valid value. """ options = OptionsConfig() self.assertEqual(None, options.cbackCommand) options.cbackCommand = "command" self.assertEqual("command", options.cbackCommand) def testConstructor_040(self): """ Test assignment of cbackCommand attribute, invalid value (empty). """ options = OptionsConfig() self.assertEqual(None, options.cbackCommand) self.failUnlessAssignRaises(ValueError, options, "cbackCommand", "") self.assertEqual(None, options.cbackCommand) def testConstructor_041(self): """ Test assignment of managedActions attribute, None value. """ options = OptionsConfig() self.assertEqual(None, options.managedActions) options.managedActions = None self.assertEqual(None, options.managedActions) def testConstructor_042(self): """ Test assignment of managedActions attribute, empty list. """ options = OptionsConfig() self.assertEqual(None, options.managedActions) options.managedActions = [] self.assertEqual([], options.managedActions) def testConstructor_043(self): """ Test assignment of managedActions attribute, non-empty list, valid values. """ options = OptionsConfig() self.assertEqual(None, options.managedActions) options.managedActions = [ "a", "b", ] self.assertEqual(["a", "b"], options.managedActions) def testConstructor_044(self): """ Test assignment of managedActions attribute, non-empty list, invalid value. """ options = OptionsConfig() self.assertEqual(None, options.managedActions) self.failUnlessAssignRaises(ValueError, options, "managedActions", ["KEN"]) self.assertEqual(None, options.managedActions) self.failUnlessAssignRaises(ValueError, options, "managedActions", ["hello, world"]) self.assertEqual(None, options.managedActions) self.failUnlessAssignRaises(ValueError, options, "managedActions", ["dash-word"]) self.assertEqual(None, options.managedActions) self.failUnlessAssignRaises(ValueError, options, "managedActions", [""]) self.assertEqual(None, options.managedActions) self.failUnlessAssignRaises(ValueError, options, "managedActions", [None]) self.assertEqual(None, options.managedActions) def testConstructor_045(self): """ Test assignment of managedActions attribute, non-empty list, mixed values. """ options = OptionsConfig() self.assertEqual(None, options.managedActions) self.failUnlessAssignRaises(ValueError, options, "managedActions", ["ken", "dash-word"]) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ options1 = OptionsConfig() options2 = OptionsConfig() self.assertEqual(options1, options2) self.assertTrue(options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(not options1 != options2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) self.assertEqual(options1, options2) self.assertTrue(options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(not options1 != options2) def testComparison_003(self): """ Test comparison of two differing objects, startingDay differs (one None). """ options1 = OptionsConfig() options2 = OptionsConfig(startingDay="monday") self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_004(self): """ Test comparison of two differing objects, startingDay differs. """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("tuesday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_005(self): """ Test comparison of two differing objects, workingDir differs (one None). """ options1 = OptionsConfig() options2 = OptionsConfig(workingDir="/tmp") self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_006(self): """ Test comparison of two differing objects, workingDir differs. """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig( "monday", "/tmp/whatever", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions ) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_007(self): """ Test comparison of two differing objects, backupUser differs (one None). """ options1 = OptionsConfig() options2 = OptionsConfig(backupUser="user") self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_008(self): """ Test comparison of two differing objects, backupUser differs. """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user2", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user1", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_009(self): """ Test comparison of two differing objects, backupGroup differs (one None). """ options1 = OptionsConfig() options2 = OptionsConfig(backupGroup="group") self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_010(self): """ Test comparison of two differing objects, backupGroup differs. """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group1", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group2", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_011(self): """ Test comparison of two differing objects, rcpCommand differs (one None). """ options1 = OptionsConfig() options2 = OptionsConfig(rcpCommand="command") self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_012(self): """ Test comparison of two differing objects, rcpCommand differs. """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -2 -B", overrides, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_013(self): """ Test comparison of two differing objects, overrides differs (one None, one empty). """ overrides1 = None overrides2 = [] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides1, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides2, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_014(self): """ Test comparison of two differing objects, overrides differs (one None, one not empty). """ overrides1 = None overrides2 = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides1, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides2, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2, "ssh") self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_015(self): """ Test comparison of two differing objects, overrides differs (one empty, one not empty). """ overrides1 = [ CommandOverride("one", "/one"), ] overrides2 = [] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides1, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides2, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_016(self): """ Test comparison of two differing objects, overrides differs (both not empty). """ overrides1 = [ CommandOverride("one", "/one"), ] overrides2 = [ CommandOverride(), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides1, hooks, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides2, hooks, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_017(self): """ Test comparison of two differing objects, hooks differs (one None, one empty). """ overrides = [ CommandOverride("one", "/one"), ] hooks1 = None hooks2 = [] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks1, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks2, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_018(self): """ Test comparison of two differing objects, hooks differs (one None, one not empty). """ overrides = [ CommandOverride("one", "/one"), ] hooks1 = [PreActionHook("collect", "ls -l ")] hooks2 = [PostActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks1, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks2, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 != options2) def testComparison_019(self): """ Test comparison of two differing objects, hooks differs (one empty, one not empty). """ overrides = [ CommandOverride("one", "/one"), ] hooks1 = [PreActionHook("collect", "ls -l ")] hooks2 = [PreActionHook("stage", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks1, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks2, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(options1 != options2) def testComparison_020(self): """ Test comparison of two differing objects, hooks differs (both not empty). """ overrides = [ CommandOverride("one", "/one"), ] hooks1 = [PreActionHook("collect", "ls -l ")] hooks2 = [PostActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks1, "ssh", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks2, "ssh", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_021(self): """ Test comparison of two differing objects, rshCommand differs (one None). """ options1 = OptionsConfig() options2 = OptionsConfig(rshCommand="command") self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_022(self): """ Test comparison of two differing objects, rshCommand differs. """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh2", "cback", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh1", "cback", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(not options1 <= options2) self.assertTrue(options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(options1 != options2) def testComparison_023(self): """ Test comparison of two differing objects, cbackCommand differs (one None). """ options1 = OptionsConfig() options2 = OptionsConfig(rshCommand="command") self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_024(self): """ Test comparison of two differing objects, cbackCommand differs. """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions = [ "collect", "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback1", managedActions) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback2", managedActions) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_025(self): """ Test comparison of two differing objects, managedActions differs (one None, one empty). """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions1 = None managedActions2 = [] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions1) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions2) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_026(self): """ Test comparison of two differing objects, managedActions differs (one None, one not empty). """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions1 = None managedActions2 = [ "collect", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions1) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions2) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(options1 != options2) def testComparison_027(self): """ Test comparison of two differing objects, managedActions differs (one empty, one not empty). """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions1 = [] managedActions2 = [ "collect", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions1) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions2) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(options1 != options2) def testComparison_028(self): """ Test comparison of two differing objects, managedActions differs (both not empty). """ overrides = [ CommandOverride("one", "/one"), ] hooks = [PreActionHook("collect", "ls -l ")] managedActions1 = [ "collect", ] managedActions2 = [ "purge", ] options1 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions1) options2 = OptionsConfig("monday", "/tmp", "user", "group", "scp -1 -B", overrides, hooks, "ssh", "cback", managedActions2) self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) #################################### # Test add and replace of overrides #################################### def testOverrides_001(self): """ Test addOverride() with no existing overrides. """ options = OptionsConfig() options.addOverride("cdrecord", "/usr/bin/wodim") self.assertEqual([CommandOverride("cdrecord", "/usr/bin/wodim")], options.overrides) def testOverrides_002(self): """ Test addOverride() with no existing override that matches. """ options = OptionsConfig() options.overrides = [ CommandOverride("one", "/one"), ] options.addOverride("cdrecord", "/usr/bin/wodim") self.assertEqual([CommandOverride("one", "/one"), CommandOverride("cdrecord", "/usr/bin/wodim")], options.overrides) def testOverrides_003(self): """ Test addOverride(), with existing override that matches. """ options = OptionsConfig() options.overrides = [ CommandOverride("cdrecord", "/one"), ] options.addOverride("cdrecord", "/usr/bin/wodim") self.assertEqual([CommandOverride("cdrecord", "/one")], options.overrides) def testOverrides_004(self): """ Test replaceOverride() with no existing overrides. """ options = OptionsConfig() options.replaceOverride("cdrecord", "/usr/bin/wodim") self.assertEqual([CommandOverride("cdrecord", "/usr/bin/wodim")], options.overrides) def testOverrides_005(self): """ Test replaceOverride() with no existing override that matches. """ options = OptionsConfig() options.overrides = [ CommandOverride("one", "/one"), ] options.replaceOverride("cdrecord", "/usr/bin/wodim") self.assertEqual([CommandOverride("one", "/one"), CommandOverride("cdrecord", "/usr/bin/wodim")], options.overrides) def testOverrides_006(self): """ Test replaceOverride(), with existing override that matches. """ options = OptionsConfig() options.overrides = [ CommandOverride("cdrecord", "/one"), ] options.replaceOverride("cdrecord", "/usr/bin/wodim") self.assertEqual([CommandOverride("cdrecord", "/usr/bin/wodim")], options.overrides) ######################## # TestPeersConfig class ######################## class TestPeersConfig(unittest.TestCase): """Tests for the PeersConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = PeersConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ peers = PeersConfig() self.assertEqual(None, peers.localPeers) self.assertEqual(None, peers.remotePeers) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values (empty lists). """ peers = PeersConfig([], []) self.assertEqual([], peers.localPeers) self.assertEqual([], peers.remotePeers) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values (non-empty lists). """ peers = PeersConfig([LocalPeer()], [RemotePeer()]) self.assertEqual([LocalPeer()], peers.localPeers) self.assertEqual([RemotePeer()], peers.remotePeers) def testConstructor_004(self): """ Test assignment of localPeers attribute, None value. """ peers = PeersConfig(localPeers=[]) self.assertEqual([], peers.localPeers) peers.localPeers = None self.assertEqual(None, peers.localPeers) def testConstructor_005(self): """ Test assignment of localPeers attribute, empty list. """ peers = PeersConfig() self.assertEqual(None, peers.localPeers) peers.localPeers = [] self.assertEqual([], peers.localPeers) def testConstructor_006(self): """ Test assignment of localPeers attribute, single valid entry. """ peers = PeersConfig() self.assertEqual(None, peers.localPeers) peers.localPeers = [ LocalPeer(), ] self.assertEqual([LocalPeer()], peers.localPeers) def testConstructor_007(self): """ Test assignment of localPeers attribute, multiple valid entries. """ peers = PeersConfig() self.assertEqual(None, peers.localPeers) peers.localPeers = [ LocalPeer(name="one"), LocalPeer(name="two"), ] self.assertEqual([LocalPeer(name="one"), LocalPeer(name="two")], peers.localPeers) def testConstructor_008(self): """ Test assignment of localPeers attribute, single invalid entry (None). """ peers = PeersConfig() self.assertEqual(None, peers.localPeers) self.failUnlessAssignRaises(ValueError, peers, "localPeers", [None]) self.assertEqual(None, peers.localPeers) def testConstructor_009(self): """ Test assignment of localPeers attribute, single invalid entry (not a LocalPeer). """ peers = PeersConfig() self.assertEqual(None, peers.localPeers) self.failUnlessAssignRaises(ValueError, peers, "localPeers", [RemotePeer()]) self.assertEqual(None, peers.localPeers) def testConstructor_010(self): """ Test assignment of localPeers attribute, mixed valid and invalid entries. """ peers = PeersConfig() self.assertEqual(None, peers.localPeers) self.failUnlessAssignRaises(ValueError, peers, "localPeers", [LocalPeer(), RemotePeer()]) self.assertEqual(None, peers.localPeers) def testConstructor_011(self): """ Test assignment of remotePeers attribute, None value. """ peers = PeersConfig(remotePeers=[]) self.assertEqual([], peers.remotePeers) peers.remotePeers = None self.assertEqual(None, peers.remotePeers) def testConstructor_012(self): """ Test assignment of remotePeers attribute, empty list. """ peers = PeersConfig() self.assertEqual(None, peers.remotePeers) peers.remotePeers = [] self.assertEqual([], peers.remotePeers) def testConstructor_013(self): """ Test assignment of remotePeers attribute, single valid entry. """ peers = PeersConfig() self.assertEqual(None, peers.remotePeers) peers.remotePeers = [ RemotePeer(name="one"), ] self.assertEqual([RemotePeer(name="one")], peers.remotePeers) def testConstructor_014(self): """ Test assignment of remotePeers attribute, multiple valid entries. """ peers = PeersConfig() self.assertEqual(None, peers.remotePeers) peers.remotePeers = [ RemotePeer(name="one"), RemotePeer(name="two"), ] self.assertEqual([RemotePeer(name="one"), RemotePeer(name="two")], peers.remotePeers) def testConstructor_015(self): """ Test assignment of remotePeers attribute, single invalid entry (None). """ peers = PeersConfig() self.assertEqual(None, peers.remotePeers) self.failUnlessAssignRaises(ValueError, peers, "remotePeers", [None]) self.assertEqual(None, peers.remotePeers) def testConstructor_016(self): """ Test assignment of remotePeers attribute, single invalid entry (not a RemotePeer). """ peers = PeersConfig() self.assertEqual(None, peers.remotePeers) self.failUnlessAssignRaises(ValueError, peers, "remotePeers", [LocalPeer()]) self.assertEqual(None, peers.remotePeers) def testConstructor_017(self): """ Test assignment of remotePeers attribute, mixed valid and invalid entries. """ peers = PeersConfig() self.assertEqual(None, peers.remotePeers) self.failUnlessAssignRaises(ValueError, peers, "remotePeers", [LocalPeer(), RemotePeer()]) self.assertEqual(None, peers.remotePeers) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ peers1 = PeersConfig() peers2 = PeersConfig() self.assertEqual(peers1, peers2) self.assertTrue(peers1 == peers2) self.assertTrue(not peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(peers1 >= peers2) self.assertTrue(not peers1 != peers2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None (empty lists). """ peers1 = PeersConfig([], []) peers2 = PeersConfig([], []) self.assertEqual(peers1, peers2) self.assertTrue(peers1 == peers2) self.assertTrue(not peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(peers1 >= peers2) self.assertTrue(not peers1 != peers2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None (non-empty lists). """ peers1 = PeersConfig([LocalPeer()], [RemotePeer()]) peers2 = PeersConfig([LocalPeer()], [RemotePeer()]) self.assertEqual(peers1, peers2) self.assertTrue(peers1 == peers2) self.assertTrue(not peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(peers1 >= peers2) self.assertTrue(not peers1 != peers2) def testComparison_004(self): """ Test comparison of two differing objects, localPeers differs (one None, one empty). """ peers1 = PeersConfig(None, [RemotePeer()]) peers2 = PeersConfig([], [RemotePeer()]) self.assertNotEqual(peers1, peers2) self.assertTrue(not peers1 == peers2) self.assertTrue(peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(not peers1 >= peers2) self.assertTrue(peers1 != peers2) def testComparison_005(self): """ Test comparison of two differing objects, localPeers differs (one None, one not empty). """ peers1 = PeersConfig(None, [RemotePeer()]) peers2 = PeersConfig([LocalPeer()], [RemotePeer()]) self.assertNotEqual(peers1, peers2) self.assertTrue(not peers1 == peers2) self.assertTrue(peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(not peers1 >= peers2) self.assertTrue(peers1 != peers2) def testComparison_006(self): """ Test comparison of two differing objects, localPeers differs (one empty, one not empty). """ peers1 = PeersConfig([], [RemotePeer()]) peers2 = PeersConfig([LocalPeer()], [RemotePeer()]) self.assertNotEqual(peers1, peers2) self.assertTrue(not peers1 == peers2) self.assertTrue(peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(not peers1 >= peers2) self.assertTrue(peers1 != peers2) def testComparison_007(self): """ Test comparison of two differing objects, localPeers differs (both not empty). """ peers1 = PeersConfig([LocalPeer(name="one")], [RemotePeer()]) peers2 = PeersConfig([LocalPeer(name="two")], [RemotePeer()]) self.assertNotEqual(peers1, peers2) self.assertTrue(not peers1 == peers2) self.assertTrue(peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(not peers1 >= peers2) self.assertTrue(peers1 != peers2) def testComparison_008(self): """ Test comparison of two differing objects, remotePeers differs (one None, one empty). """ peers1 = PeersConfig([LocalPeer()], None) peers2 = PeersConfig([LocalPeer()], []) self.assertNotEqual(peers1, peers2) self.assertTrue(not peers1 == peers2) self.assertTrue(peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(not peers1 >= peers2) self.assertTrue(peers1 != peers2) def testComparison_009(self): """ Test comparison of two differing objects, remotePeers differs (one None, one not empty). """ peers1 = PeersConfig([LocalPeer()], None) peers2 = PeersConfig([LocalPeer()], [RemotePeer()]) self.assertNotEqual(peers1, peers2) self.assertTrue(not peers1 == peers2) self.assertTrue(peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(not peers1 >= peers2) self.assertTrue(peers1 != peers2) def testComparison_010(self): """ Test comparison of two differing objects, remotePeers differs (one empty, one not empty). """ peers1 = PeersConfig([LocalPeer()], []) peers2 = PeersConfig([LocalPeer()], [RemotePeer()]) self.assertNotEqual(peers1, peers2) self.assertTrue(not peers1 == peers2) self.assertTrue(peers1 < peers2) self.assertTrue(peers1 <= peers2) self.assertTrue(not peers1 > peers2) self.assertTrue(not peers1 >= peers2) self.assertTrue(peers1 != peers2) def testComparison_011(self): """ Test comparison of two differing objects, remotePeers differs (both not empty). """ peers1 = PeersConfig([LocalPeer()], [RemotePeer(name="two")]) peers2 = PeersConfig([LocalPeer()], [RemotePeer(name="one")]) self.assertNotEqual(peers1, peers2) self.assertTrue(not peers1 == peers2) self.assertTrue(not peers1 < peers2) self.assertTrue(not peers1 <= peers2) self.assertTrue(peers1 > peers2) self.assertTrue(peers1 >= peers2) self.assertTrue(peers1 != peers2) ########################## # TestCollectConfig class ########################## class TestCollectConfig(unittest.TestCase): """Tests for the CollectConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = CollectConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ collect = CollectConfig() self.assertEqual(None, collect.targetDir) self.assertEqual(None, collect.collectMode) self.assertEqual(None, collect.archiveMode) self.assertEqual(None, collect.ignoreFile) self.assertEqual(None, collect.absoluteExcludePaths) self.assertEqual(None, collect.excludePatterns) self.assertEqual(None, collect.collectDirs) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values (lists empty). """ collect = CollectConfig("/target", "incr", "tar", "ignore", [], [], [], []) self.assertEqual("/target", collect.targetDir) self.assertEqual("incr", collect.collectMode) self.assertEqual("tar", collect.archiveMode) self.assertEqual("ignore", collect.ignoreFile) self.assertEqual([], collect.absoluteExcludePaths) self.assertEqual([], collect.excludePatterns) self.assertEqual([], collect.collectDirs) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values (lists not empty). """ collect = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertEqual("/target", collect.targetDir) self.assertEqual("incr", collect.collectMode) self.assertEqual("tar", collect.archiveMode) self.assertEqual("ignore", collect.ignoreFile) self.assertEqual(["/path"], collect.absoluteExcludePaths) self.assertEqual(["pattern"], collect.excludePatterns) self.assertEqual([CollectFile()], collect.collectFiles) self.assertEqual([CollectDir()], collect.collectDirs) def testConstructor_004(self): """ Test assignment of targetDir attribute, None value. """ collect = CollectConfig(targetDir="/whatever") self.assertEqual("/whatever", collect.targetDir) collect.targetDir = None self.assertEqual(None, collect.targetDir) def testConstructor_005(self): """ Test assignment of targetDir attribute, valid value. """ collect = CollectConfig() self.assertEqual(None, collect.targetDir) collect.targetDir = "/whatever" self.assertEqual("/whatever", collect.targetDir) def testConstructor_006(self): """ Test assignment of targetDir attribute, invalid value (empty). """ collect = CollectConfig() self.assertEqual(None, collect.targetDir) self.failUnlessAssignRaises(ValueError, collect, "targetDir", "") self.assertEqual(None, collect.targetDir) def testConstructor_007(self): """ Test assignment of targetDir attribute, invalid value (non-absolute). """ collect = CollectConfig() self.assertEqual(None, collect.targetDir) self.failUnlessAssignRaises(ValueError, collect, "targetDir", "bogus") self.assertEqual(None, collect.targetDir) def testConstructor_008(self): """ Test assignment of collectMode attribute, None value. """ collect = CollectConfig(collectMode="incr") self.assertEqual("incr", collect.collectMode) collect.collectMode = None self.assertEqual(None, collect.collectMode) def testConstructor_009(self): """ Test assignment of collectMode attribute, valid value. """ collect = CollectConfig() self.assertEqual(None, collect.collectMode) collect.collectMode = "daily" self.assertEqual("daily", collect.collectMode) collect.collectMode = "weekly" self.assertEqual("weekly", collect.collectMode) collect.collectMode = "incr" self.assertEqual("incr", collect.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, invalid value (empty). """ collect = CollectConfig() self.assertEqual(None, collect.collectMode) self.failUnlessAssignRaises(ValueError, collect, "collectMode", "") self.assertEqual(None, collect.collectMode) def testConstructor_011(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ collect = CollectConfig() self.assertEqual(None, collect.collectMode) self.failUnlessAssignRaises(ValueError, collect, "collectMode", "periodic") self.assertEqual(None, collect.collectMode) def testConstructor_012(self): """ Test assignment of archiveMode attribute, None value. """ collect = CollectConfig(archiveMode="tar") self.assertEqual("tar", collect.archiveMode) collect.archiveMode = None self.assertEqual(None, collect.archiveMode) def testConstructor_013(self): """ Test assignment of archiveMode attribute, valid value. """ collect = CollectConfig() self.assertEqual(None, collect.archiveMode) collect.archiveMode = "tar" self.assertEqual("tar", collect.archiveMode) collect.archiveMode = "targz" self.assertEqual("targz", collect.archiveMode) collect.archiveMode = "tarbz2" self.assertEqual("tarbz2", collect.archiveMode) def testConstructor_014(self): """ Test assignment of archiveMode attribute, invalid value (empty). """ collect = CollectConfig() self.assertEqual(None, collect.archiveMode) self.failUnlessAssignRaises(ValueError, collect, "archiveMode", "") self.assertEqual(None, collect.archiveMode) def testConstructor_015(self): """ Test assignment of archiveMode attribute, invalid value (not in list). """ collect = CollectConfig() self.assertEqual(None, collect.archiveMode) self.failUnlessAssignRaises(ValueError, collect, "archiveMode", "tarz") self.assertEqual(None, collect.archiveMode) def testConstructor_016(self): """ Test assignment of ignoreFile attribute, None value. """ collect = CollectConfig(ignoreFile="ignore") self.assertEqual("ignore", collect.ignoreFile) collect.ignoreFile = None self.assertEqual(None, collect.ignoreFile) def testConstructor_017(self): """ Test assignment of ignoreFile attribute, valid value. """ collect = CollectConfig() self.assertEqual(None, collect.ignoreFile) collect.ignoreFile = "ignore" self.assertEqual("ignore", collect.ignoreFile) def testConstructor_018(self): """ Test assignment of ignoreFile attribute, invalid value (empty). """ collect = CollectConfig() self.assertEqual(None, collect.ignoreFile) self.failUnlessAssignRaises(ValueError, collect, "ignoreFile", "") self.assertEqual(None, collect.ignoreFile) def testConstructor_019(self): """ Test assignment of absoluteExcludePaths attribute, None value. """ collect = CollectConfig(absoluteExcludePaths=[]) self.assertEqual([], collect.absoluteExcludePaths) collect.absoluteExcludePaths = None self.assertEqual(None, collect.absoluteExcludePaths) def testConstructor_020(self): """ Test assignment of absoluteExcludePaths attribute, [] value. """ collect = CollectConfig() self.assertEqual(None, collect.absoluteExcludePaths) collect.absoluteExcludePaths = [] self.assertEqual([], collect.absoluteExcludePaths) def testConstructor_021(self): """ Test assignment of absoluteExcludePaths attribute, single valid entry. """ collect = CollectConfig() self.assertEqual(None, collect.absoluteExcludePaths) collect.absoluteExcludePaths = [ "/whatever", ] self.assertEqual(["/whatever"], collect.absoluteExcludePaths) def testConstructor_022(self): """ Test assignment of absoluteExcludePaths attribute, multiple valid entries. """ collect = CollectConfig() self.assertEqual(None, collect.absoluteExcludePaths) collect.absoluteExcludePaths = [ "/one", "/two", "/three", ] self.assertEqual(["/one", "/two", "/three"], collect.absoluteExcludePaths) def testConstructor_023(self): """ Test assignment of absoluteExcludePaths attribute, single invalid entry (empty). """ collect = CollectConfig() self.assertEqual(None, collect.absoluteExcludePaths) self.failUnlessAssignRaises(ValueError, collect, "absoluteExcludePaths", [""]) self.assertEqual(None, collect.absoluteExcludePaths) def testConstructor_024(self): """ Test assignment of absoluteExcludePaths attribute, single invalid entry (not absolute). """ collect = CollectConfig() self.assertEqual(None, collect.absoluteExcludePaths) self.failUnlessAssignRaises(ValueError, collect, "absoluteExcludePaths", ["one"]) self.assertEqual(None, collect.absoluteExcludePaths) def testConstructor_025(self): """ Test assignment of absoluteExcludePaths attribute, mixed valid and invalid entries. """ collect = CollectConfig() self.assertEqual(None, collect.absoluteExcludePaths) self.failUnlessAssignRaises(ValueError, collect, "absoluteExcludePaths", ["one", "/two"]) self.assertEqual(None, collect.absoluteExcludePaths) def testConstructor_026(self): """ Test assignment of excludePatterns attribute, None value. """ collect = CollectConfig(excludePatterns=[]) self.assertEqual([], collect.excludePatterns) collect.excludePatterns = None self.assertEqual(None, collect.excludePatterns) def testConstructor_027(self): """ Test assignment of excludePatterns attribute, [] value. """ collect = CollectConfig() self.assertEqual(None, collect.excludePatterns) collect.excludePatterns = [] self.assertEqual([], collect.excludePatterns) def testConstructor_028(self): """ Test assignment of excludePatterns attribute, single valid entry. """ collect = CollectConfig() self.assertEqual(None, collect.excludePatterns) collect.excludePatterns = [ "pattern", ] self.assertEqual(["pattern"], collect.excludePatterns) def testConstructor_029(self): """ Test assignment of excludePatterns attribute, multiple valid entries. """ collect = CollectConfig() self.assertEqual(None, collect.excludePatterns) collect.excludePatterns = [ "pattern1", "pattern2", ] self.assertEqual(["pattern1", "pattern2"], collect.excludePatterns) def testConstructor_029a(self): """ Test assignment of excludePatterns attribute, single invalid entry. """ collect = CollectConfig() self.assertEqual(None, collect.excludePatterns) self.failUnlessAssignRaises(ValueError, collect, "excludePatterns", ["*.jpg"]) self.assertEqual(None, collect.excludePatterns) def testConstructor_029b(self): """ Test assignment of excludePatterns attribute, multiple invalid entries. """ collect = CollectConfig() self.assertEqual(None, collect.excludePatterns) self.failUnlessAssignRaises(ValueError, collect, "excludePatterns", ["*.jpg", "*"]) self.assertEqual(None, collect.excludePatterns) def testConstructor_029c(self): """ Test assignment of excludePatterns attribute, mixed valid and invalid entries. """ collect = CollectConfig() self.assertEqual(None, collect.excludePatterns) self.failUnlessAssignRaises(ValueError, collect, "excludePatterns", ["*.jpg", "valid"]) self.assertEqual(None, collect.excludePatterns) def testConstructor_030(self): """ Test assignment of collectDirs attribute, None value. """ collect = CollectConfig(collectDirs=[]) self.assertEqual([], collect.collectDirs) collect.collectDirs = None self.assertEqual(None, collect.collectDirs) def testConstructor_031(self): """ Test assignment of collectDirs attribute, [] value. """ collect = CollectConfig() self.assertEqual(None, collect.collectDirs) collect.collectDirs = [] self.assertEqual([], collect.collectDirs) def testConstructor_032(self): """ Test assignment of collectDirs attribute, single valid entry. """ collect = CollectConfig() self.assertEqual(None, collect.collectDirs) collect.collectDirs = [ CollectDir(absolutePath="/one"), ] self.assertEqual([CollectDir(absolutePath="/one")], collect.collectDirs) def testConstructor_033(self): """ Test assignment of collectDirs attribute, multiple valid entries. """ collect = CollectConfig() self.assertEqual(None, collect.collectDirs) collect.collectDirs = [ CollectDir(absolutePath="/one"), CollectDir(absolutePath="/two"), ] self.assertEqual([CollectDir(absolutePath="/one"), CollectDir(absolutePath="/two")], collect.collectDirs) def testConstructor_034(self): """ Test assignment of collectDirs attribute, single invalid entry (None). """ collect = CollectConfig() self.assertEqual(None, collect.collectDirs) self.failUnlessAssignRaises(ValueError, collect, "collectDirs", [None]) self.assertEqual(None, collect.collectDirs) def testConstructor_035(self): """ Test assignment of collectDirs attribute, single invalid entry (not a CollectDir). """ collect = CollectConfig() self.assertEqual(None, collect.collectDirs) self.failUnlessAssignRaises(ValueError, collect, "collectDirs", ["hello"]) self.assertEqual(None, collect.collectDirs) def testConstructor_036(self): """ Test assignment of collectDirs attribute, mixed valid and invalid entries. """ collect = CollectConfig() self.assertEqual(None, collect.collectDirs) self.failUnlessAssignRaises(ValueError, collect, "collectDirs", ["hello", CollectDir()]) self.assertEqual(None, collect.collectDirs) def testConstructor_037(self): """ Test assignment of collectFiles attribute, None value. """ collect = CollectConfig(collectFiles=[]) self.assertEqual([], collect.collectFiles) collect.collectFiles = None self.assertEqual(None, collect.collectFiles) def testConstructor_038(self): """ Test assignment of collectFiles attribute, [] value. """ collect = CollectConfig() self.assertEqual(None, collect.collectFiles) collect.collectFiles = [] self.assertEqual([], collect.collectFiles) def testConstructor_039(self): """ Test assignment of collectFiles attribute, single valid entry. """ collect = CollectConfig() self.assertEqual(None, collect.collectFiles) collect.collectFiles = [ CollectFile(absolutePath="/one"), ] self.assertEqual([CollectFile(absolutePath="/one")], collect.collectFiles) def testConstructor_040(self): """ Test assignment of collectFiles attribute, multiple valid entries. """ collect = CollectConfig() self.assertEqual(None, collect.collectFiles) collect.collectFiles = [ CollectFile(absolutePath="/one"), CollectFile(absolutePath="/two"), ] self.assertEqual([CollectFile(absolutePath="/one"), CollectFile(absolutePath="/two")], collect.collectFiles) def testConstructor_041(self): """ Test assignment of collectFiles attribute, single invalid entry (None). """ collect = CollectConfig() self.assertEqual(None, collect.collectFiles) self.failUnlessAssignRaises(ValueError, collect, "collectFiles", [None]) self.assertEqual(None, collect.collectFiles) def testConstructor_042(self): """ Test assignment of collectFiles attribute, single invalid entry (not a CollectFile). """ collect = CollectConfig() self.assertEqual(None, collect.collectFiles) self.failUnlessAssignRaises(ValueError, collect, "collectFiles", ["hello"]) self.assertEqual(None, collect.collectFiles) def testConstructor_043(self): """ Test assignment of collectFiles attribute, mixed valid and invalid entries. """ collect = CollectConfig() self.assertEqual(None, collect.collectFiles) self.failUnlessAssignRaises(ValueError, collect, "collectFiles", ["hello", CollectFile()]) self.assertEqual(None, collect.collectFiles) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ collect1 = CollectConfig() collect2 = CollectConfig() self.assertEqual(collect1, collect2) self.assertTrue(collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(not collect1 != collect2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertEqual(collect1, collect2) self.assertTrue(collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(not collect1 != collect2) def testComparison_003(self): """ Test comparison of two differing objects, targetDir differs (one None). """ collect1 = CollectConfig(None, "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target2", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_004(self): """ Test comparison of two differing objects, targetDir differs. """ collect1 = CollectConfig("/target1", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target2", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_005(self): """ Test comparison of two differing objects, collectMode differs (one None). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", None, "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs. """ collect1 = CollectConfig("/target", "daily", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_007(self): """ Test comparison of two differing objects, archiveMode differs (one None). """ collect1 = CollectConfig("/target", "incr", None, "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_008(self): """ Test comparison of two differing objects, archiveMode differs. """ collect1 = CollectConfig("/target", "incr", "targz", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tarbz2", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_009(self): """ Test comparison of two differing objects, ignoreFile differs (one None). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", None, ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_010(self): """ Test comparison of two differing objects, ignoreFile differs. """ collect1 = CollectConfig("/target", "incr", "tar", "ignore1", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore2", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_011(self): """ Test comparison of two differing objects, absoluteExcludePaths differs (one None, one empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", None, ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", [], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_012(self): """ Test comparison of two differing objects, absoluteExcludePaths differs (one None, one not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", None, ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_013(self): """ Test comparison of two differing objects, absoluteExcludePaths differs (one empty, one not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", [], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_014(self): """ Test comparison of two differing objects, absoluteExcludePaths differs (both not empty). """ collect1 = CollectConfig( "/target", "incr", "tar", "ignore", ["/path", "/path2"], ["pattern"], [CollectFile()], [CollectDir()] ) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_015(self): """ Test comparison of two differing objects, excludePatterns differs (one None, one empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], None, [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], [], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_016(self): """ Test comparison of two differing objects, excludePatterns differs (one None, one not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], None, [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_017(self): """ Test comparison of two differing objects, excludePatterns differs (one empty, one not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], [], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_018(self): """ Test comparison of two differing objects, excludePatterns differs (both not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig( "/target", "incr", "tar", "ignore", ["/path"], ["pattern", "bogus"], [CollectFile()], [CollectDir()] ) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_019(self): """ Test comparison of two differing objects, collectDirs differs (one None, one empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], None) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], []) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_020(self): """ Test comparison of two differing objects, collectDirs differs (one None, one not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], None) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_021(self): """ Test comparison of two differing objects, collectDirs differs (one empty, one not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], []) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_022(self): """ Test comparison of two differing objects, collectDirs differs (both not empty). """ collect1 = CollectConfig( "/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir(), CollectDir()] ) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_023(self): """ Test comparison of two differing objects, collectFiles differs (one None, one empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], None, [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_024(self): """ Test comparison of two differing objects, collectFiles differs (one None, one not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], None, [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(collect1 < collect2) self.assertTrue(collect1 <= collect2) self.assertTrue(not collect1 > collect2) self.assertTrue(not collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_025(self): """ Test comparison of two differing objects, collectFiles differs (one empty, one not empty). """ collect1 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) def testComparison_026(self): """ Test comparison of two differing objects, collectFiles differs (both not empty). """ collect1 = CollectConfig( "/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile(), CollectFile()], [CollectDir()] ) collect2 = CollectConfig("/target", "incr", "tar", "ignore", ["/path"], ["pattern"], [CollectFile()], [CollectDir()]) self.assertNotEqual(collect1, collect2) self.assertTrue(not collect1 == collect2) self.assertTrue(not collect1 < collect2) self.assertTrue(not collect1 <= collect2) self.assertTrue(collect1 > collect2) self.assertTrue(collect1 >= collect2) self.assertTrue(collect1 != collect2) ######################## # TestStageConfig class ######################## class TestStageConfig(unittest.TestCase): """Tests for the StageConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = StageConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ stage = StageConfig() self.assertEqual(None, stage.targetDir) self.assertEqual(None, stage.localPeers) self.assertEqual(None, stage.remotePeers) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values (empty lists). """ stage = StageConfig("/whatever", [], []) self.assertEqual("/whatever", stage.targetDir) self.assertEqual([], stage.localPeers) self.assertEqual([], stage.remotePeers) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values (non-empty lists). """ stage = StageConfig("/whatever", [LocalPeer()], [RemotePeer()]) self.assertEqual("/whatever", stage.targetDir) self.assertEqual([LocalPeer()], stage.localPeers) self.assertEqual([RemotePeer()], stage.remotePeers) def testConstructor_004(self): """ Test assignment of targetDir attribute, None value. """ stage = StageConfig(targetDir="/whatever") self.assertEqual("/whatever", stage.targetDir) stage.targetDir = None self.assertEqual(None, stage.targetDir) def testConstructor_005(self): """ Test assignment of targetDir attribute, valid value. """ stage = StageConfig() self.assertEqual(None, stage.targetDir) stage.targetDir = "/whatever" self.assertEqual("/whatever", stage.targetDir) def testConstructor_006(self): """ Test assignment of targetDir attribute, invalid value (empty). """ stage = StageConfig() self.assertEqual(None, stage.targetDir) self.failUnlessAssignRaises(ValueError, stage, "targetDir", "") self.assertEqual(None, stage.targetDir) def testConstructor_007(self): """ Test assignment of targetDir attribute, invalid value (non-absolute). """ stage = StageConfig() self.assertEqual(None, stage.targetDir) self.failUnlessAssignRaises(ValueError, stage, "targetDir", "stuff") self.assertEqual(None, stage.targetDir) def testConstructor_008(self): """ Test assignment of localPeers attribute, None value. """ stage = StageConfig(localPeers=[]) self.assertEqual([], stage.localPeers) stage.localPeers = None self.assertEqual(None, stage.localPeers) def testConstructor_009(self): """ Test assignment of localPeers attribute, empty list. """ stage = StageConfig() self.assertEqual(None, stage.localPeers) stage.localPeers = [] self.assertEqual([], stage.localPeers) def testConstructor_010(self): """ Test assignment of localPeers attribute, single valid entry. """ stage = StageConfig() self.assertEqual(None, stage.localPeers) stage.localPeers = [ LocalPeer(), ] self.assertEqual([LocalPeer()], stage.localPeers) def testConstructor_011(self): """ Test assignment of localPeers attribute, multiple valid entries. """ stage = StageConfig() self.assertEqual(None, stage.localPeers) stage.localPeers = [ LocalPeer(name="one"), LocalPeer(name="two"), ] self.assertEqual([LocalPeer(name="one"), LocalPeer(name="two")], stage.localPeers) def testConstructor_012(self): """ Test assignment of localPeers attribute, single invalid entry (None). """ stage = StageConfig() self.assertEqual(None, stage.localPeers) self.failUnlessAssignRaises(ValueError, stage, "localPeers", [None]) self.assertEqual(None, stage.localPeers) def testConstructor_013(self): """ Test assignment of localPeers attribute, single invalid entry (not a LocalPeer). """ stage = StageConfig() self.assertEqual(None, stage.localPeers) self.failUnlessAssignRaises(ValueError, stage, "localPeers", [RemotePeer()]) self.assertEqual(None, stage.localPeers) def testConstructor_014(self): """ Test assignment of localPeers attribute, mixed valid and invalid entries. """ stage = StageConfig() self.assertEqual(None, stage.localPeers) self.failUnlessAssignRaises(ValueError, stage, "localPeers", [LocalPeer(), RemotePeer()]) self.assertEqual(None, stage.localPeers) def testConstructor_015(self): """ Test assignment of remotePeers attribute, None value. """ stage = StageConfig(remotePeers=[]) self.assertEqual([], stage.remotePeers) stage.remotePeers = None self.assertEqual(None, stage.remotePeers) def testConstructor_016(self): """ Test assignment of remotePeers attribute, empty list. """ stage = StageConfig() self.assertEqual(None, stage.remotePeers) stage.remotePeers = [] self.assertEqual([], stage.remotePeers) def testConstructor_017(self): """ Test assignment of remotePeers attribute, single valid entry. """ stage = StageConfig() self.assertEqual(None, stage.remotePeers) stage.remotePeers = [ RemotePeer(name="one"), ] self.assertEqual([RemotePeer(name="one")], stage.remotePeers) def testConstructor_018(self): """ Test assignment of remotePeers attribute, multiple valid entries. """ stage = StageConfig() self.assertEqual(None, stage.remotePeers) stage.remotePeers = [ RemotePeer(name="one"), RemotePeer(name="two"), ] self.assertEqual([RemotePeer(name="one"), RemotePeer(name="two")], stage.remotePeers) def testConstructor_019(self): """ Test assignment of remotePeers attribute, single invalid entry (None). """ stage = StageConfig() self.assertEqual(None, stage.remotePeers) self.failUnlessAssignRaises(ValueError, stage, "remotePeers", [None]) self.assertEqual(None, stage.remotePeers) def testConstructor_020(self): """ Test assignment of remotePeers attribute, single invalid entry (not a RemotePeer). """ stage = StageConfig() self.assertEqual(None, stage.remotePeers) self.failUnlessAssignRaises(ValueError, stage, "remotePeers", [LocalPeer()]) self.assertEqual(None, stage.remotePeers) def testConstructor_021(self): """ Test assignment of remotePeers attribute, mixed valid and invalid entries. """ stage = StageConfig() self.assertEqual(None, stage.remotePeers) self.failUnlessAssignRaises(ValueError, stage, "remotePeers", [LocalPeer(), RemotePeer()]) self.assertEqual(None, stage.remotePeers) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ stage1 = StageConfig() stage2 = StageConfig() self.assertEqual(stage1, stage2) self.assertTrue(stage1 == stage2) self.assertTrue(not stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(stage1 >= stage2) self.assertTrue(not stage1 != stage2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None (empty lists). """ stage1 = StageConfig("/target", [], []) stage2 = StageConfig("/target", [], []) self.assertEqual(stage1, stage2) self.assertTrue(stage1 == stage2) self.assertTrue(not stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(stage1 >= stage2) self.assertTrue(not stage1 != stage2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None (non-empty lists). """ stage1 = StageConfig("/target", [LocalPeer()], [RemotePeer()]) stage2 = StageConfig("/target", [LocalPeer()], [RemotePeer()]) self.assertEqual(stage1, stage2) self.assertTrue(stage1 == stage2) self.assertTrue(not stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(stage1 >= stage2) self.assertTrue(not stage1 != stage2) def testComparison_004(self): """ Test comparison of two differing objects, targetDir differs (one None). """ stage1 = StageConfig() stage2 = StageConfig(targetDir="/whatever") self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_005(self): """ Test comparison of two differing objects, targetDir differs. """ stage1 = StageConfig("/target1", [LocalPeer()], [RemotePeer()]) stage2 = StageConfig("/target2", [LocalPeer()], [RemotePeer()]) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_006(self): """ Test comparison of two differing objects, localPeers differs (one None, one empty). """ stage1 = StageConfig("/target", None, [RemotePeer()]) stage2 = StageConfig("/target", [], [RemotePeer()]) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_007(self): """ Test comparison of two differing objects, localPeers differs (one None, one not empty). """ stage1 = StageConfig("/target", None, [RemotePeer()]) stage2 = StageConfig("/target", [LocalPeer()], [RemotePeer()]) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_008(self): """ Test comparison of two differing objects, localPeers differs (one empty, one not empty). """ stage1 = StageConfig("/target", [], [RemotePeer()]) stage2 = StageConfig("/target", [LocalPeer()], [RemotePeer()]) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_009(self): """ Test comparison of two differing objects, localPeers differs (both not empty). """ stage1 = StageConfig("/target", [LocalPeer(name="one")], [RemotePeer()]) stage2 = StageConfig("/target", [LocalPeer(name="two")], [RemotePeer()]) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_010(self): """ Test comparison of two differing objects, remotePeers differs (one None, one empty). """ stage1 = StageConfig("/target", [LocalPeer()], None) stage2 = StageConfig("/target", [LocalPeer()], []) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_011(self): """ Test comparison of two differing objects, remotePeers differs (one None, one not empty). """ stage1 = StageConfig("/target", [LocalPeer()], None) stage2 = StageConfig("/target", [LocalPeer()], [RemotePeer()]) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_012(self): """ Test comparison of two differing objects, remotePeers differs (one empty, one not empty). """ stage1 = StageConfig("/target", [LocalPeer()], []) stage2 = StageConfig("/target", [LocalPeer()], [RemotePeer()]) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(stage1 < stage2) self.assertTrue(stage1 <= stage2) self.assertTrue(not stage1 > stage2) self.assertTrue(not stage1 >= stage2) self.assertTrue(stage1 != stage2) def testComparison_013(self): """ Test comparison of two differing objects, remotePeers differs (both not empty). """ stage1 = StageConfig("/target", [LocalPeer()], [RemotePeer(name="two")]) stage2 = StageConfig("/target", [LocalPeer()], [RemotePeer(name="one")]) self.assertNotEqual(stage1, stage2) self.assertTrue(not stage1 == stage2) self.assertTrue(not stage1 < stage2) self.assertTrue(not stage1 <= stage2) self.assertTrue(stage1 > stage2) self.assertTrue(stage1 >= stage2) self.assertTrue(stage1 != stage2) ######################## # TestStoreConfig class ######################## class TestStoreConfig(unittest.TestCase): """Tests for the StoreConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = StoreConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ store = StoreConfig() self.assertEqual(None, store.sourceDir) self.assertEqual(None, store.mediaType) self.assertEqual(None, store.deviceType) self.assertEqual(None, store.devicePath) self.assertEqual(None, store.deviceScsiId) self.assertEqual(None, store.driveSpeed) self.assertEqual(False, store.checkData) self.assertEqual(False, store.checkMedia) self.assertEqual(False, store.warnMidnite) self.assertEqual(False, store.noEject) self.assertEqual(None, store.blankBehavior) self.assertEqual(None, store.refreshMediaDelay) self.assertEqual(None, store.ejectDelay) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ behavior = BlankBehavior("weekly", "1.3") store = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior, 12, 13) self.assertEqual("/source", store.sourceDir) self.assertEqual("cdr-74", store.mediaType) self.assertEqual("cdwriter", store.deviceType) self.assertEqual("/dev/cdrw", store.devicePath) self.assertEqual("0,0,0", store.deviceScsiId) self.assertEqual(4, store.driveSpeed) self.assertEqual(True, store.checkData) self.assertEqual(True, store.checkMedia) self.assertEqual(True, store.warnMidnite) self.assertEqual(True, store.noEject) self.assertEqual(behavior, store.blankBehavior) self.assertEqual(12, store.refreshMediaDelay) self.assertEqual(13, store.ejectDelay) def testConstructor_003(self): """ Test assignment of sourceDir attribute, None value. """ store = StoreConfig(sourceDir="/whatever") self.assertEqual("/whatever", store.sourceDir) store.sourceDir = None self.assertEqual(None, store.sourceDir) def testConstructor_004(self): """ Test assignment of sourceDir attribute, valid value. """ store = StoreConfig() self.assertEqual(None, store.sourceDir) store.sourceDir = "/whatever" self.assertEqual("/whatever", store.sourceDir) def testConstructor_005(self): """ Test assignment of sourceDir attribute, invalid value (empty). """ store = StoreConfig() self.assertEqual(None, store.sourceDir) self.failUnlessAssignRaises(ValueError, store, "sourceDir", "") self.assertEqual(None, store.sourceDir) def testConstructor_006(self): """ Test assignment of sourceDir attribute, invalid value (non-absolute). """ store = StoreConfig() self.assertEqual(None, store.sourceDir) self.failUnlessAssignRaises(ValueError, store, "sourceDir", "bogus") self.assertEqual(None, store.sourceDir) def testConstructor_007(self): """ Test assignment of mediaType attribute, None value. """ store = StoreConfig(mediaType="cdr-74") self.assertEqual("cdr-74", store.mediaType) store.mediaType = None self.assertEqual(None, store.mediaType) def testConstructor_008(self): """ Test assignment of mediaType attribute, valid value. """ store = StoreConfig() self.assertEqual(None, store.mediaType) store.mediaType = "cdr-74" self.assertEqual("cdr-74", store.mediaType) store.mediaType = "cdrw-74" self.assertEqual("cdrw-74", store.mediaType) store.mediaType = "cdr-80" self.assertEqual("cdr-80", store.mediaType) store.mediaType = "cdrw-80" self.assertEqual("cdrw-80", store.mediaType) store.mediaType = "dvd+r" self.assertEqual("dvd+r", store.mediaType) store.mediaType = "dvd+rw" self.assertEqual("dvd+rw", store.mediaType) def testConstructor_009(self): """ Test assignment of mediaType attribute, invalid value (empty). """ store = StoreConfig() self.assertEqual(None, store.mediaType) self.failUnlessAssignRaises(ValueError, store, "mediaType", "") self.assertEqual(None, store.mediaType) def testConstructor_010(self): """ Test assignment of mediaType attribute, invalid value (not in list). """ store = StoreConfig() self.assertEqual(None, store.mediaType) self.failUnlessAssignRaises(ValueError, store, "mediaType", "floppy") self.assertEqual(None, store.mediaType) def testConstructor_011(self): """ Test assignment of deviceType attribute, None value. """ store = StoreConfig(deviceType="cdwriter") self.assertEqual("cdwriter", store.deviceType) store.deviceType = None self.assertEqual(None, store.deviceType) def testConstructor_012(self): """ Test assignment of deviceType attribute, valid value. """ store = StoreConfig() self.assertEqual(None, store.deviceType) store.deviceType = "cdwriter" self.assertEqual("cdwriter", store.deviceType) store.deviceType = "dvdwriter" self.assertEqual("dvdwriter", store.deviceType) def testConstructor_013(self): """ Test assignment of deviceType attribute, invalid value (empty). """ store = StoreConfig() self.assertEqual(None, store.deviceType) self.failUnlessAssignRaises(ValueError, store, "deviceType", "") self.assertEqual(None, store.deviceType) def testConstructor_014(self): """ Test assignment of deviceType attribute, invalid value (not in list). """ store = StoreConfig() self.assertEqual(None, store.deviceType) self.failUnlessAssignRaises(ValueError, store, "deviceType", "ftape") self.assertEqual(None, store.deviceType) def testConstructor_015(self): """ Test assignment of devicePath attribute, None value. """ store = StoreConfig(devicePath="/dev/cdrw") self.assertEqual("/dev/cdrw", store.devicePath) store.devicePath = None self.assertEqual(None, store.devicePath) def testConstructor_016(self): """ Test assignment of devicePath attribute, valid value. """ store = StoreConfig() self.assertEqual(None, store.devicePath) store.devicePath = "/dev/cdrw" self.assertEqual("/dev/cdrw", store.devicePath) def testConstructor_017(self): """ Test assignment of devicePath attribute, invalid value (empty). """ store = StoreConfig() self.assertEqual(None, store.devicePath) self.failUnlessAssignRaises(ValueError, store, "devicePath", "") self.assertEqual(None, store.devicePath) def testConstructor_018(self): """ Test assignment of devicePath attribute, invalid value (non-absolute). """ store = StoreConfig() self.assertEqual(None, store.devicePath) self.failUnlessAssignRaises(ValueError, store, "devicePath", "dev/cdrw") self.assertEqual(None, store.devicePath) def testConstructor_019(self): """ Test assignment of deviceScsiId attribute, None value. """ store = StoreConfig(deviceScsiId="0,0,0") self.assertEqual("0,0,0", store.deviceScsiId) store.deviceScsiId = None self.assertEqual(None, store.deviceScsiId) def testConstructor_020(self): """ Test assignment of deviceScsiId attribute, valid value. """ store = StoreConfig() self.assertEqual(None, store.deviceScsiId) store.deviceScsiId = "0,0,0" self.assertEqual("0,0,0", store.deviceScsiId) store.deviceScsiId = "ATA:0,0,0" self.assertEqual("ATA:0,0,0", store.deviceScsiId) def testConstructor_021(self): """ Test assignment of deviceScsiId attribute, invalid value (empty). """ store = StoreConfig() self.assertEqual(None, store.deviceScsiId) self.failUnlessAssignRaises(ValueError, store, "deviceScsiId", "") self.assertEqual(None, store.deviceScsiId) def testConstructor_022(self): """ Test assignment of deviceScsiId attribute, invalid value (invalid id). """ store = StoreConfig() self.assertEqual(None, store.deviceScsiId) self.failUnlessAssignRaises(ValueError, store, "deviceScsiId", "ATA;0,0,0") self.assertEqual(None, store.deviceScsiId) self.failUnlessAssignRaises(ValueError, store, "deviceScsiId", "ATAPI-0,0,0") self.assertEqual(None, store.deviceScsiId) self.failUnlessAssignRaises(ValueError, store, "deviceScsiId", "1:2:3") self.assertEqual(None, store.deviceScsiId) def testConstructor_023(self): """ Test assignment of driveSpeed attribute, None value. """ store = StoreConfig(driveSpeed=4) self.assertEqual(4, store.driveSpeed) store.driveSpeed = None self.assertEqual(None, store.driveSpeed) def testConstructor_024(self): """ Test assignment of driveSpeed attribute, valid value. """ store = StoreConfig() self.assertEqual(None, store.driveSpeed) store.driveSpeed = 4 self.assertEqual(4, store.driveSpeed) store.driveSpeed = "12" self.assertEqual(12, store.driveSpeed) def testConstructor_025(self): """ Test assignment of driveSpeed attribute, invalid value (not an integer). """ store = StoreConfig() self.assertEqual(None, store.driveSpeed) self.failUnlessAssignRaises(ValueError, store, "driveSpeed", "blech") self.assertEqual(None, store.driveSpeed) self.failUnlessAssignRaises(ValueError, store, "driveSpeed", CollectDir()) self.assertEqual(None, store.driveSpeed) def testConstructor_026(self): """ Test assignment of checkData attribute, None value. """ store = StoreConfig(checkData=True) self.assertEqual(True, store.checkData) store.checkData = None self.assertEqual(False, store.checkData) def testConstructor_027(self): """ Test assignment of checkData attribute, valid value (real boolean). """ store = StoreConfig() self.assertEqual(False, store.checkData) store.checkData = True self.assertEqual(True, store.checkData) store.checkData = False self.assertEqual(False, store.checkData) def testConstructor_028(self): """ Test assignment of checkData attribute, valid value (expression). """ store = StoreConfig() self.assertEqual(False, store.checkData) store.checkData = 0 self.assertEqual(False, store.checkData) store.checkData = [] self.assertEqual(False, store.checkData) store.checkData = None self.assertEqual(False, store.checkData) store.checkData = ["a"] self.assertEqual(True, store.checkData) store.checkData = 3 self.assertEqual(True, store.checkData) def testConstructor_029(self): """ Test assignment of warnMidnite attribute, None value. """ store = StoreConfig(warnMidnite=True) self.assertEqual(True, store.warnMidnite) store.warnMidnite = None self.assertEqual(False, store.warnMidnite) def testConstructor_030(self): """ Test assignment of warnMidnite attribute, valid value (real boolean). """ store = StoreConfig() self.assertEqual(False, store.warnMidnite) store.warnMidnite = True self.assertEqual(True, store.warnMidnite) store.warnMidnite = False self.assertEqual(False, store.warnMidnite) def testConstructor_031(self): """ Test assignment of warnMidnite attribute, valid value (expression). """ store = StoreConfig() self.assertEqual(False, store.warnMidnite) store.warnMidnite = 0 self.assertEqual(False, store.warnMidnite) store.warnMidnite = [] self.assertEqual(False, store.warnMidnite) store.warnMidnite = None self.assertEqual(False, store.warnMidnite) store.warnMidnite = ["a"] self.assertEqual(True, store.warnMidnite) store.warnMidnite = 3 self.assertEqual(True, store.warnMidnite) def testConstructor_032(self): """ Test assignment of noEject attribute, None value. """ store = StoreConfig(noEject=True) self.assertEqual(True, store.noEject) store.noEject = None self.assertEqual(False, store.noEject) def testConstructor_033(self): """ Test assignment of noEject attribute, valid value (real boolean). """ store = StoreConfig() self.assertEqual(False, store.noEject) store.noEject = True self.assertEqual(True, store.noEject) store.noEject = False self.assertEqual(False, store.noEject) def testConstructor_034(self): """ Test assignment of noEject attribute, valid value (expression). """ store = StoreConfig() self.assertEqual(False, store.noEject) store.noEject = 0 self.assertEqual(False, store.noEject) store.noEject = [] self.assertEqual(False, store.noEject) store.noEject = None self.assertEqual(False, store.noEject) store.noEject = ["a"] self.assertEqual(True, store.noEject) store.noEject = 3 self.assertEqual(True, store.noEject) def testConstructor_035(self): """ Test assignment of checkMedia attribute, None value. """ store = StoreConfig(checkMedia=True) self.assertEqual(True, store.checkMedia) store.checkMedia = None self.assertEqual(False, store.checkMedia) def testConstructor_036(self): """ Test assignment of checkMedia attribute, valid value (real boolean). """ store = StoreConfig() self.assertEqual(False, store.checkMedia) store.checkMedia = True self.assertEqual(True, store.checkMedia) store.checkMedia = False self.assertEqual(False, store.checkMedia) def testConstructor_037(self): """ Test assignment of checkMedia attribute, valid value (expression). """ store = StoreConfig() self.assertEqual(False, store.checkMedia) store.checkMedia = 0 self.assertEqual(False, store.checkMedia) store.checkMedia = [] self.assertEqual(False, store.checkMedia) store.checkMedia = None self.assertEqual(False, store.checkMedia) store.checkMedia = ["a"] self.assertEqual(True, store.checkMedia) store.checkMedia = 3 self.assertEqual(True, store.checkMedia) def testConstructor_038(self): """ Test assignment of blankBehavior attribute, None value. """ store = StoreConfig() store.blankBehavior = None self.assertEqual(None, store.blankBehavior) def testConstructor_039(self): """ Test assignment of blankBehavior store attribute, valid value. """ store = StoreConfig() store.blankBehavior = BlankBehavior() self.assertEqual(BlankBehavior(), store.blankBehavior) def testConstructor_040(self): """ Test assignment of blankBehavior store attribute, invalid value (not BlankBehavior). """ store = StoreConfig() self.failUnlessAssignRaises(ValueError, store, "blankBehavior", CollectDir()) def testConstructor_041(self): """ Test assignment of refreshMediaDelay attribute, None value. """ store = StoreConfig(refreshMediaDelay=4) self.assertEqual(4, store.refreshMediaDelay) store.refreshMediaDelay = None self.assertEqual(None, store.refreshMediaDelay) def testConstructor_042(self): """ Test assignment of refreshMediaDelay attribute, valid value. """ store = StoreConfig() self.assertEqual(None, store.refreshMediaDelay) store.refreshMediaDelay = 4 self.assertEqual(4, store.refreshMediaDelay) store.refreshMediaDelay = "12" self.assertEqual(12, store.refreshMediaDelay) store.refreshMediaDelay = "0" self.assertEqual(None, store.refreshMediaDelay) store.refreshMediaDelay = 0 self.assertEqual(None, store.refreshMediaDelay) def testConstructor_043(self): """ Test assignment of refreshMediaDelay attribute, invalid value (not an integer). """ store = StoreConfig() self.assertEqual(None, store.refreshMediaDelay) self.failUnlessAssignRaises(ValueError, store, "refreshMediaDelay", "blech") self.assertEqual(None, store.refreshMediaDelay) self.failUnlessAssignRaises(ValueError, store, "refreshMediaDelay", CollectDir()) self.assertEqual(None, store.refreshMediaDelay) def testConstructor_044(self): """ Test assignment of ejectDelay attribute, None value. """ store = StoreConfig(ejectDelay=4) self.assertEqual(4, store.ejectDelay) store.ejectDelay = None self.assertEqual(None, store.ejectDelay) def testConstructor_045(self): """ Test assignment of ejectDelay attribute, valid value. """ store = StoreConfig() self.assertEqual(None, store.ejectDelay) store.ejectDelay = 4 self.assertEqual(4, store.ejectDelay) store.ejectDelay = "12" self.assertEqual(12, store.ejectDelay) store.ejectDelay = "0" self.assertEqual(None, store.ejectDelay) store.ejectDelay = 0 self.assertEqual(None, store.ejectDelay) def testConstructor_046(self): """ Test assignment of ejectDelay attribute, invalid value (not an integer). """ store = StoreConfig() self.assertEqual(None, store.ejectDelay) self.failUnlessAssignRaises(ValueError, store, "ejectDelay", "blech") self.assertEqual(None, store.ejectDelay) self.failUnlessAssignRaises(ValueError, store, "ejectDelay", CollectDir()) self.assertEqual(None, store.ejectDelay) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ store1 = StoreConfig() store2 = StoreConfig() self.assertEqual(store1, store2) self.assertTrue(store1 == store2) self.assertTrue(not store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(store1 >= store2) self.assertTrue(not store1 != store2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertEqual(store1, store2) self.assertTrue(store1 == store2) self.assertTrue(not store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(store1 >= store2) self.assertTrue(not store1 != store2) def testComparison_003(self): """ Test comparison of two differing objects, sourceDir differs (one None). """ store1 = StoreConfig() store2 = StoreConfig(sourceDir="/whatever") self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_004(self): """ Test comparison of two differing objects, sourceDir differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source1", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior1, 4, 5) store2 = StoreConfig("/source2", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_005(self): """ Test comparison of two differing objects, mediaType differs (one None). """ store1 = StoreConfig() store2 = StoreConfig(mediaType="cdr-74") self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_006(self): """ Test comparison of two differing objects, mediaType differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdrw-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(not store1 < store2) self.assertTrue(not store1 <= store2) self.assertTrue(store1 > store2) self.assertTrue(store1 >= store2) self.assertTrue(store1 != store2) def testComparison_007(self): """ Test comparison of two differing objects, deviceType differs (one None). """ store1 = StoreConfig() store2 = StoreConfig(deviceType="cdwriter") self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_008(self): """ Test comparison of two differing objects, devicePath differs (one None). """ store1 = StoreConfig() store2 = StoreConfig(devicePath="/dev/cdrw") self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_009(self): """ Test comparison of two differing objects, devicePath differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/hdd", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_010(self): """ Test comparison of two differing objects, deviceScsiId differs (one None). """ store1 = StoreConfig() store2 = StoreConfig(deviceScsiId="0,0,0") self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_011(self): """ Test comparison of two differing objects, deviceScsiId differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "ATA:0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_012(self): """ Test comparison of two differing objects, driveSpeed differs (one None). """ store1 = StoreConfig() store2 = StoreConfig(driveSpeed=3) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_013(self): """ Test comparison of two differing objects, driveSpeed differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 1, True, True, True, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_014(self): """ Test comparison of two differing objects, checkData differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, False, True, True, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_015(self): """ Test comparison of two differing objects, warnMidnite differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, False, True, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_016(self): """ Test comparison of two differing objects, noEject differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, False, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_017(self): """ Test comparison of two differing objects, checkMedia differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, False, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_018(self): """ Test comparison of two differing objects, blankBehavior differs (one None). """ behavior = BlankBehavior() store1 = StoreConfig() store2 = StoreConfig(blankBehavior=behavior) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_019(self): """ Test comparison of two differing objects, blankBehavior differs. """ behavior1 = BlankBehavior("daily", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior1, 4, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 4, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_020(self): """ Test comparison of two differing objects, refreshMediaDelay differs (one None). """ store1 = StoreConfig() store2 = StoreConfig(refreshMediaDelay=3) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_021(self): """ Test comparison of two differing objects, refreshMediaDelay differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 1, True, True, True, True, behavior1, 1, 5) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 1, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_022(self): """ Test comparison of two differing objects, ejectDelay differs (one None). """ store1 = StoreConfig() store2 = StoreConfig(ejectDelay=3) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) def testComparison_023(self): """ Test comparison of two differing objects, ejectDelay differs. """ behavior1 = BlankBehavior("weekly", "1.3") behavior2 = BlankBehavior("weekly", "1.3") store1 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 1, True, True, True, True, behavior1, 4, 1) store2 = StoreConfig("/source", "cdr-74", "cdwriter", "/dev/cdrw", "0,0,0", 1, True, True, True, True, behavior2, 4, 5) self.assertNotEqual(store1, store2) self.assertTrue(not store1 == store2) self.assertTrue(store1 < store2) self.assertTrue(store1 <= store2) self.assertTrue(not store1 > store2) self.assertTrue(not store1 >= store2) self.assertTrue(store1 != store2) ######################## # TestPurgeConfig class ######################## class TestPurgeConfig(unittest.TestCase): """Tests for the PurgeConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = PurgeConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ purge = PurgeConfig() self.assertEqual(None, purge.purgeDirs) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values (empty list). """ purge = PurgeConfig([]) self.assertEqual([], purge.purgeDirs) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values (non-empty list). """ purge = PurgeConfig([PurgeDir()]) self.assertEqual([PurgeDir()], purge.purgeDirs) def testConstructor_004(self): """ Test assignment of purgeDirs attribute, None value. """ purge = PurgeConfig([]) self.assertEqual([], purge.purgeDirs) purge.purgeDirs = None self.assertEqual(None, purge.purgeDirs) def testConstructor_005(self): """ Test assignment of purgeDirs attribute, [] value. """ purge = PurgeConfig() self.assertEqual(None, purge.purgeDirs) purge.purgeDirs = [] self.assertEqual([], purge.purgeDirs) def testConstructor_006(self): """ Test assignment of purgeDirs attribute, single valid entry. """ purge = PurgeConfig() self.assertEqual(None, purge.purgeDirs) purge.purgeDirs = [ PurgeDir(), ] self.assertEqual([PurgeDir()], purge.purgeDirs) def testConstructor_007(self): """ Test assignment of purgeDirs attribute, multiple valid entries. """ purge = PurgeConfig() self.assertEqual(None, purge.purgeDirs) purge.purgeDirs = [ PurgeDir("/one"), PurgeDir("/two"), ] self.assertEqual([PurgeDir("/one"), PurgeDir("/two")], purge.purgeDirs) def testConstructor_009(self): """ Test assignment of purgeDirs attribute, single invalid entry (not a PurgeDir). """ purge = PurgeConfig() self.assertEqual(None, purge.purgeDirs) self.failUnlessAssignRaises(ValueError, purge, "purgeDirs", [RemotePeer()]) self.assertEqual(None, purge.purgeDirs) def testConstructor_010(self): """ Test assignment of purgeDirs attribute, mixed valid and invalid entries. """ purge = PurgeConfig() self.assertEqual(None, purge.purgeDirs) self.failUnlessAssignRaises(ValueError, purge, "purgeDirs", [PurgeDir(), RemotePeer()]) self.assertEqual(None, purge.purgeDirs) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ purge1 = PurgeConfig() purge2 = PurgeConfig() self.assertEqual(purge1, purge2) self.assertTrue(purge1 == purge2) self.assertTrue(not purge1 < purge2) self.assertTrue(purge1 <= purge2) self.assertTrue(not purge1 > purge2) self.assertTrue(purge1 >= purge2) self.assertTrue(not purge1 != purge2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None (empty lists). """ purge1 = PurgeConfig([]) purge2 = PurgeConfig([]) self.assertEqual(purge1, purge2) self.assertTrue(purge1 == purge2) self.assertTrue(not purge1 < purge2) self.assertTrue(purge1 <= purge2) self.assertTrue(not purge1 > purge2) self.assertTrue(purge1 >= purge2) self.assertTrue(not purge1 != purge2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None (non-empty lists). """ purge1 = PurgeConfig([PurgeDir()]) purge2 = PurgeConfig([PurgeDir()]) self.assertEqual(purge1, purge2) self.assertTrue(purge1 == purge2) self.assertTrue(not purge1 < purge2) self.assertTrue(purge1 <= purge2) self.assertTrue(not purge1 > purge2) self.assertTrue(purge1 >= purge2) self.assertTrue(not purge1 != purge2) def testComparison_004(self): """ Test comparison of two differing objects, purgeDirs differs (one None, one empty). """ purge1 = PurgeConfig(None) purge2 = PurgeConfig([]) self.assertNotEqual(purge1, purge2) self.assertTrue(not purge1 == purge2) self.assertTrue(purge1 < purge2) self.assertTrue(purge1 <= purge2) self.assertTrue(not purge1 > purge2) self.assertTrue(not purge1 >= purge2) self.assertTrue(purge1 != purge2) def testComparison_005(self): """ Test comparison of two differing objects, purgeDirs differs (one None, one not empty). """ purge1 = PurgeConfig(None) purge2 = PurgeConfig([PurgeDir()]) self.assertNotEqual(purge1, purge2) self.assertTrue(not purge1 == purge2) self.assertTrue(purge1 < purge2) self.assertTrue(purge1 <= purge2) self.assertTrue(not purge1 > purge2) self.assertTrue(not purge1 >= purge2) self.assertTrue(purge1 != purge2) def testComparison_006(self): """ Test comparison of two differing objects, purgeDirs differs (one empty, one not empty). """ purge1 = PurgeConfig([]) purge2 = PurgeConfig([PurgeDir()]) self.assertNotEqual(purge1, purge2) self.assertTrue(not purge1 == purge2) self.assertTrue(purge1 < purge2) self.assertTrue(purge1 <= purge2) self.assertTrue(not purge1 > purge2) self.assertTrue(not purge1 >= purge2) self.assertTrue(purge1 != purge2) def testComparison_007(self): """ Test comparison of two differing objects, purgeDirs differs (both not empty). """ purge1 = PurgeConfig([PurgeDir("/two")]) purge2 = PurgeConfig([PurgeDir("/one")]) self.assertNotEqual(purge1, purge2) self.assertTrue(not purge1 == purge2) self.assertTrue(not purge1 < purge2) self.assertTrue(not purge1 <= purge2) self.assertTrue(purge1 > purge2) self.assertTrue(purge1 >= purge2) self.assertTrue(purge1 != purge2) ################### # TestConfig class ################### class TestConfig(unittest.TestCase): """Tests for the Config class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################ # Setup methods ################ def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = Config() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = Config(validate=False) self.assertEqual(None, config.reference) self.assertEqual(None, config.extensions) self.assertEqual(None, config.options) self.assertEqual(None, config.peers) self.assertEqual(None, config.collect) self.assertEqual(None, config.stage) self.assertEqual(None, config.store) self.assertEqual(None, config.purge) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = Config(validate=True) self.assertEqual(None, config.reference) self.assertEqual(None, config.extensions) self.assertEqual(None, config.options) self.assertEqual(None, config.peers) self.assertEqual(None, config.collect) self.assertEqual(None, config.stage) self.assertEqual(None, config.store) self.assertEqual(None, config.purge) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["cback.conf.2"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, Config, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test with empty config document as data, validate=False. """ path = self.resources["cback.conf.2"] with open(path) as f: contents = f.read() config = Config(xmlData=contents, validate=False) self.assertEqual(None, config.reference) self.assertEqual(None, config.extensions) self.assertEqual(None, config.options) self.assertEqual(None, config.peers) self.assertEqual(None, config.collect) self.assertEqual(None, config.stage) self.assertEqual(None, config.store) self.assertEqual(None, config.purge) def testConstructor_005(self): """ Test with empty config document in a file, validate=False. """ path = self.resources["cback.conf.2"] config = Config(xmlPath=path, validate=False) self.assertEqual(None, config.reference) self.assertEqual(None, config.extensions) self.assertEqual(None, config.options) self.assertEqual(None, config.peers) self.assertEqual(None, config.collect) self.assertEqual(None, config.stage) self.assertEqual(None, config.store) self.assertEqual(None, config.purge) def testConstructor_006(self): """ Test assignment of reference attribute, None value. """ config = Config() config.reference = None self.assertEqual(None, config.reference) def testConstructor_007(self): """ Test assignment of reference attribute, valid value. """ config = Config() config.reference = ReferenceConfig() self.assertEqual(ReferenceConfig(), config.reference) def testConstructor_008(self): """ Test assignment of reference attribute, invalid value (not ReferenceConfig). """ config = Config() self.failUnlessAssignRaises(ValueError, config, "reference", CollectDir()) def testConstructor_009(self): """ Test assignment of extensions attribute, None value. """ config = Config() config.extensions = None self.assertEqual(None, config.extensions) def testConstructor_010(self): """ Test assignment of extensions attribute, valid value. """ config = Config() config.extensions = ExtensionsConfig() self.assertEqual(ExtensionsConfig(), config.extensions) def testConstructor_011(self): """ Test assignment of extensions attribute, invalid value (not ExtensionsConfig). """ config = Config() self.failUnlessAssignRaises(ValueError, config, "extensions", CollectDir()) def testConstructor_012(self): """ Test assignment of options attribute, None value. """ config = Config() config.options = None self.assertEqual(None, config.options) def testConstructor_013(self): """ Test assignment of options attribute, valid value. """ config = Config() config.options = OptionsConfig() self.assertEqual(OptionsConfig(), config.options) def testConstructor_014(self): """ Test assignment of options attribute, invalid value (not OptionsConfig). """ config = Config() self.failUnlessAssignRaises(ValueError, config, "options", CollectDir()) def testConstructor_015(self): """ Test assignment of collect attribute, None value. """ config = Config() config.collect = None self.assertEqual(None, config.collect) def testConstructor_016(self): """ Test assignment of collect attribute, valid value. """ config = Config() config.collect = CollectConfig() self.assertEqual(CollectConfig(), config.collect) def testConstructor_017(self): """ Test assignment of collect attribute, invalid value (not CollectConfig). """ config = Config() self.failUnlessAssignRaises(ValueError, config, "collect", CollectDir()) def testConstructor_018(self): """ Test assignment of stage attribute, None value. """ config = Config() config.stage = None self.assertEqual(None, config.stage) def testConstructor_019(self): """ Test assignment of stage attribute, valid value. """ config = Config() config.stage = StageConfig() self.assertEqual(StageConfig(), config.stage) def testConstructor_020(self): """ Test assignment of stage attribute, invalid value (not StageConfig). """ config = Config() self.failUnlessAssignRaises(ValueError, config, "stage", CollectDir()) def testConstructor_021(self): """ Test assignment of store attribute, None value. """ config = Config() config.store = None self.assertEqual(None, config.store) def testConstructor_022(self): """ Test assignment of store attribute, valid value. """ config = Config() config.store = StoreConfig() self.assertEqual(StoreConfig(), config.store) def testConstructor_023(self): """ Test assignment of store attribute, invalid value (not StoreConfig). """ config = Config() self.failUnlessAssignRaises(ValueError, config, "store", CollectDir()) def testConstructor_024(self): """ Test assignment of purge attribute, None value. """ config = Config() config.purge = None self.assertEqual(None, config.purge) def testConstructor_025(self): """ Test assignment of purge attribute, valid value. """ config = Config() config.purge = PurgeConfig() self.assertEqual(PurgeConfig(), config.purge) def testConstructor_026(self): """ Test assignment of purge attribute, invalid value (not PurgeConfig). """ config = Config() self.failUnlessAssignRaises(ValueError, config, "purge", CollectDir()) def testConstructor_027(self): """ Test assignment of peers attribute, None value. """ config = Config() config.peers = None self.assertEqual(None, config.peers) def testConstructor_028(self): """ Test assignment of peers attribute, valid value. """ config = Config() config.peers = PeersConfig() self.assertEqual(PeersConfig(), config.peers) def testConstructor_029(self): """ Test assignment of peers attribute, invalid value (not PeersConfig). """ config = Config() self.failUnlessAssignRaises(ValueError, config, "peers", CollectDir()) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = Config() config2 = Config() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = Config() config1.reference = ReferenceConfig() config1.extensions = ExtensionsConfig() config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.extensions = ExtensionsConfig() config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, reference differs (one None). """ config1 = Config() config2 = Config() config2.reference = ReferenceConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, reference differs. """ config1 = Config() config1.reference = ReferenceConfig(author="one") config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig(author="two") config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_005(self): """ Test comparison of two differing objects, extensions differs (one None). """ config1 = Config() config2 = Config() config2.extensions = ExtensionsConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_006(self): """ Test comparison of two differing objects, extensions differs (one list empty, one None). """ config1 = Config() config1.reference = ReferenceConfig() config1.extensions = ExtensionsConfig(None) config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.extensions = ExtensionsConfig([]) config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_007(self): """ Test comparison of two differing objects, extensions differs (one list empty, one not empty). """ config1 = Config() config1.reference = ReferenceConfig() config1.extensions = ExtensionsConfig([]) config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.extensions = ExtensionsConfig([ExtendedAction("one", "two", "three")]) config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_008(self): """ Test comparison of two differing objects, extensions differs (both lists not empty). """ config1 = Config() config1.reference = ReferenceConfig() config1.extensions = ExtensionsConfig([ExtendedAction("one", "two", "three")]) config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.extensions = ExtensionsConfig([ExtendedAction("one", "two", "four")]) config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(not config1 <= config2) self.assertTrue(config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(config1 != config2) def testComparison_009(self): """ Test comparison of two differing objects, options differs (one None). """ config1 = Config() config2 = Config() config2.options = OptionsConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_010(self): """ Test comparison of two differing objects, options differs. """ config1 = Config() config1.reference = ReferenceConfig() config1.options = OptionsConfig(startingDay="tuesday") config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.options = OptionsConfig(startingDay="monday") config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(not config1 <= config2) self.assertTrue(config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(config1 != config2) def testComparison_011(self): """ Test comparison of two differing objects, collect differs (one None). """ config1 = Config() config2 = Config() config2.collect = CollectConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_012(self): """ Test comparison of two differing objects, collect differs. """ config1 = Config() config1.reference = ReferenceConfig() config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig(collectMode="daily") config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig(collectMode="incr") config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_013(self): """ Test comparison of two differing objects, stage differs (one None). """ config1 = Config() config2 = Config() config2.stage = StageConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_014(self): """ Test comparison of two differing objects, stage differs. """ config1 = Config() config1.reference = ReferenceConfig() config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig(targetDir="/something") config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig(targetDir="/whatever") config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_015(self): """ Test comparison of two differing objects, store differs (one None). """ config1 = Config() config2 = Config() config2.store = StoreConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_016(self): """ Test comparison of two differing objects, store differs. """ config1 = Config() config1.reference = ReferenceConfig() config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig(deviceScsiId="ATA:0,0,0") config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig(deviceScsiId="0,0,0") config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(not config1 <= config2) self.assertTrue(config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(config1 != config2) def testComparison_017(self): """ Test comparison of two differing objects, purge differs (one None). """ config1 = Config() config2 = Config() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_018(self): """ Test comparison of two differing objects, purge differs. """ config1 = Config() config1.reference = ReferenceConfig() config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig(purgeDirs=None) config2 = Config() config2.reference = ReferenceConfig() config2.options = OptionsConfig() config2.peers = PeersConfig() config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig(purgeDirs=[]) self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_019(self): """ Test comparison of two differing objects, peers differs (one None). """ config1 = Config() config2 = Config() config2.peers = PeersConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_020(self): """ Test comparison of two identical objects, peers differs. """ config1 = Config() config1.reference = ReferenceConfig() config1.extensions = ExtensionsConfig() config1.options = OptionsConfig() config1.peers = PeersConfig() config1.collect = CollectConfig() config1.stage = StageConfig() config1.store = StoreConfig() config1.purge = PurgeConfig() config2 = Config() config2.reference = ReferenceConfig() config2.extensions = ExtensionsConfig() config2.options = OptionsConfig() config2.peers = PeersConfig(localPeers=[LocalPeer()]) config2.collect = CollectConfig() config2.stage = StageConfig() config2.store = StoreConfig() config2.purge = PurgeConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on an empty reference section. """ config = Config() config.reference = ReferenceConfig() config._validateReference() def testValidate_002(self): """ Test validate on a non-empty reference section, with everything filled in. """ config = Config() config.reference = ReferenceConfig("author", "revision", "description", "generator") config._validateReference() def testValidate_003(self): """ Test validate on an empty extensions section, with a None list. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = None config._validateExtensions() def testValidate_004(self): """ Test validate on an empty extensions section, with [] for the list. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = [] config._validateExtensions() def testValidate_005(self): """ Test validate on an a extensions section, with one empty extended action. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = [ ExtendedAction(), ] self.assertRaises(ValueError, config._validateExtensions) def testValidate_006(self): """ Test validate on an a extensions section, with one extended action that has only a name. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = [ ExtendedAction(name="name"), ] self.assertRaises(ValueError, config._validateExtensions) def testValidate_007(self): """ Test validate on an a extensions section, with one extended action that has only a module. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = [ ExtendedAction(module="module"), ] self.assertRaises(ValueError, config._validateExtensions) def testValidate_008(self): """ Test validate on an a extensions section, with one extended action that has only a function. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = [ ExtendedAction(function="function"), ] self.assertRaises(ValueError, config._validateExtensions) def testValidate_009(self): """ Test validate on an a extensions section, with one extended action that has only an index. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = [ ExtendedAction(index=12), ] self.assertRaises(ValueError, config._validateExtensions) def testValidate_010(self): """ Test validate on an a extensions section, with one extended action that makes sense, index order mode. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = "index" config.extensions.actions = [ExtendedAction("one", "two", "three", 100)] config._validateExtensions() def testValidate_011(self): """ Test validate on an a extensions section, with one extended action that makes sense, dependency order mode. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = "dependency" config.extensions.actions = [ExtendedAction("one", "two", "three", dependencies=ActionDependencies())] config._validateExtensions() def testValidate_012(self): """ Test validate on an a extensions section, with several extended actions that make sense for various kinds of order modes. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = [ ExtendedAction("a", "b", "c", 1), ExtendedAction("e", "f", "g", 10), ] config._validateExtensions() config.extensions = ExtensionsConfig() config.extensions.orderMode = "index" config.extensions.actions = [ ExtendedAction("a", "b", "c", 1), ExtendedAction("e", "f", "g", 10), ] config._validateExtensions() config.extensions = ExtensionsConfig() config.extensions.orderMode = "dependency" config.extensions.actions = [ ExtendedAction("a", "b", "c", dependencies=ActionDependencies()), ExtendedAction("e", "f", "g", dependencies=ActionDependencies()), ] config._validateExtensions() def testValidate_012a(self): """ Test validate on an a extensions section, with several extended actions that don't have the proper ordering modes. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = None config.extensions.actions = [ ExtendedAction("a", "b", "c", dependencies=ActionDependencies()), ExtendedAction("e", "f", "g", dependencies=ActionDependencies()), ] self.assertRaises(ValueError, config._validateExtensions) config.extensions = ExtensionsConfig() config.extensions.orderMode = "index" config.extensions.actions = [ ExtendedAction("a", "b", "c", dependencies=ActionDependencies()), ExtendedAction("e", "f", "g", dependencies=ActionDependencies()), ] self.assertRaises(ValueError, config._validateExtensions) config.extensions = ExtensionsConfig() config.extensions.orderMode = "dependency" config.extensions.actions = [ ExtendedAction("a", "b", "c", 100), ExtendedAction("e", "f", "g", 12), ] self.assertRaises(ValueError, config._validateExtensions) config.extensions = ExtensionsConfig() config.extensions.orderMode = "index" config.extensions.actions = [ ExtendedAction("a", "b", "c", 12), ExtendedAction("e", "f", "g", dependencies=ActionDependencies()), ] self.assertRaises(ValueError, config._validateExtensions) config.extensions = ExtensionsConfig() config.extensions.orderMode = "dependency" config.extensions.actions = [ ExtendedAction("a", "b", "c", dependencies=ActionDependencies()), ExtendedAction("e", "f", "g", 12), ] self.assertRaises(ValueError, config._validateExtensions) def testValidate_013(self): """ Test validate on an empty options section. """ config = Config() config.options = OptionsConfig() self.assertRaises(ValueError, config._validateOptions) def testValidate_014(self): """ Test validate on a non-empty options section, with everything filled in. """ config = Config() config.options = OptionsConfig("monday", "/whatever", "user", "group", "command") config._validateOptions() def testValidate_015(self): """ Test validate on a non-empty options section, with individual items missing. """ config = Config() config.options = OptionsConfig("monday", "/whatever", "user", "group", "command") config._validateOptions() config.options = OptionsConfig("monday", "/whatever", "user", "group", "command") config.options.startingDay = None self.assertRaises(ValueError, config._validateOptions) config.options = OptionsConfig("monday", "/whatever", "user", "group", "command") config.options.workingDir = None self.assertRaises(ValueError, config._validateOptions) config.options = OptionsConfig("monday", "/whatever", "user", "group", "command") config.options.backupUser = None self.assertRaises(ValueError, config._validateOptions) config.options = OptionsConfig("monday", "/whatever", "user", "group", "command") config.options.backupGroup = None self.assertRaises(ValueError, config._validateOptions) config.options = OptionsConfig("monday", "/whatever", "user", "group", "command") config.options.rcpCommand = None self.assertRaises(ValueError, config._validateOptions) def testValidate_016(self): """ Test validate on an empty collect section. """ config = Config() config.collect = CollectConfig() self.assertRaises(ValueError, config._validateCollect) def testValidate_017(self): """ Test validate on collect section containing only targetDir. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config._validateCollect() # we no longer validate that at least one file or dir is required here def testValidate_018(self): """ Test validate on collect section containing only targetDir and one collectDirs entry that is empty. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir(), ] self.assertRaises(ValueError, config._validateCollect) def testValidate_018a(self): """ Test validate on collect section containing only targetDir and one collectFiles entry that is empty. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectFiles = [ CollectFile(), ] self.assertRaises(ValueError, config._validateCollect) def testValidate_019(self): """ Test validate on collect section containing only targetDir and one collectDirs entry with only a path. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir(absolutePath="/stuff"), ] self.assertRaises(ValueError, config._validateCollect) def testValidate_019a(self): """ Test validate on collect section containing only targetDir and one collectFiles entry with only a path. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectFiles = [ CollectFile(absolutePath="/stuff"), ] self.assertRaises(ValueError, config._validateCollect) def testValidate_020(self): """ Test validate on collect section containing only targetDir and one collectDirs entry with path, collect mode, archive mode and ignore file. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir(absolutePath="/stuff", collectMode="incr", archiveMode="tar", ignoreFile="i"), ] config._validateCollect() def testValidate_020a(self): """ Test validate on collect section containing only targetDir and one collectFiles entry with path, collect mode and archive mode. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectFiles = [ CollectFile(absolutePath="/stuff", collectMode="incr", archiveMode="tar"), ] config._validateCollect() def testValidate_021(self): """ Test validate on collect section containing targetDir, collect mode, archive mode and ignore file, and one collectDirs entry with only a path. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectMode = "incr" config.collect.archiveMode = "tar" config.collect.ignoreFile = "ignore" config.collect.collectDirs = [ CollectDir(absolutePath="/stuff"), ] config._validateCollect() def testValidate_021a(self): """ Test validate on collect section containing targetDir, collect mode, archive mode and ignore file, and one collectFiles entry with only a path. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectMode = "incr" config.collect.archiveMode = "tar" config.collect.ignoreFile = "ignore" config.collect.collectFiles = [ CollectFile(absolutePath="/stuff"), ] config._validateCollect() def testValidate_022(self): """ Test validate on collect section containing targetDir, but with collect mode, archive mode and ignore file mixed between main section and directories. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.archiveMode = "tar" config.collect.ignoreFile = "ignore" config.collect.collectDirs = [ CollectDir(absolutePath="/stuff", collectMode="incr", ignoreFile="i"), ] config._validateCollect() config.collect.collectDirs.append(CollectDir(absolutePath="/stuff2")) self.assertRaises(ValueError, config._validateCollect) config.collect.collectDirs[-1].collectMode = "daily" config._validateCollect() def testValidate_022a(self): """ Test validate on collect section containing targetDir, but with collect mode, and archive mode mixed between main section and directories. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.archiveMode = "tar" config.collect.collectFiles = [ CollectFile(absolutePath="/stuff", collectMode="incr", archiveMode="targz"), ] config._validateCollect() config.collect.collectFiles.append(CollectFile(absolutePath="/stuff2")) self.assertRaises(ValueError, config._validateCollect) config.collect.collectFiles[-1].collectMode = "daily" config._validateCollect() def testValidate_023(self): """ Test validate on an empty stage section. """ config = Config() config.stage = StageConfig() self.assertRaises(ValueError, config._validateStage) def testValidate_024(self): """ Test validate on stage section containing only targetDir and None for the lists. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = None config.stage.remotePeers = None self.assertRaises(ValueError, config._validateStage) def testValidate_025(self): """ Test validate on stage section containing only targetDir and [] for the lists. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [] config.stage.remotePeers = [] self.assertRaises(ValueError, config._validateStage) def testValidate_026(self): """ Test validate on stage section containing targetDir and one local peer that is empty. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(), ] self.assertRaises(ValueError, config._validateStage) def testValidate_027(self): """ Test validate on stage section containing targetDir and one local peer with only a name. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(name="name"), ] self.assertRaises(ValueError, config._validateStage) def testValidate_028(self): """ Test validate on stage section containing targetDir and one local peer with a name and path, None for remote list. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(name="name", collectDir="/somewhere"), ] config.stage.remotePeers = None config._validateStage() def testValidate_029(self): """ Test validate on stage section containing targetDir and one local peer with a name and path, [] for remote list. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(name="name", collectDir="/somewhere"), ] config.stage.remotePeers = [] config._validateStage() def testValidate_030(self): """ Test validate on stage section containing targetDir and one remote peer that is empty. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.remotePeers = [ RemotePeer(), ] self.assertRaises(ValueError, config._validateStage) def testValidate_031(self): """ Test validate on stage section containing targetDir and one remote peer with only a name. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.remotePeers = [ RemotePeer(name="blech"), ] self.assertRaises(ValueError, config._validateStage) def testValidate_032(self): """ Test validate on stage section containing targetDir and one remote peer with a name and path, None for local list. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = None config.stage.remotePeers = [ RemotePeer(name="blech", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validateStage) config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config._validateStage() config.options = None self.assertRaises(ValueError, config._validateStage) config.stage.remotePeers[-1].remoteUser = "remote" config.stage.remotePeers[-1].rcpCommand = "command" config._validateStage() def testValidate_033(self): """ Test validate on stage section containing targetDir and one remote peer with a name and path, [] for local list. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [] config.stage.remotePeers = [ RemotePeer(name="blech", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validateStage) config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config._validateStage() config.options = None self.assertRaises(ValueError, config._validateStage) config.stage.remotePeers[-1].remoteUser = "remote" config.stage.remotePeers[-1].rcpCommand = "command" config._validateStage() def testValidate_034(self): """ Test validate on stage section containing targetDir and one remote and one local peer. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(name="metoo", collectDir="/nowhere"), ] config.stage.remotePeers = [ RemotePeer(name="blech", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validateStage) config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config._validateStage() config.options = None self.assertRaises(ValueError, config._validateStage) config.stage.remotePeers[-1].remoteUser = "remote" config.stage.remotePeers[-1].rcpCommand = "command" config._validateStage() def testValidate_035(self): """ Test validate on stage section containing targetDir multiple remote and local peers. """ config = Config() config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(name="metoo", collectDir="/nowhere"), LocalPeer("one", "/two"), LocalPeer("a", "/b"), ] config.stage.remotePeers = [ RemotePeer(name="blech", collectDir="/some/path/to/data"), RemotePeer("c", "/d"), ] self.assertRaises(ValueError, config._validateStage) config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config._validateStage() config.options = None self.assertRaises(ValueError, config._validateStage) config.stage.remotePeers[-1].remoteUser = "remote" config.stage.remotePeers[-1].rcpCommand = "command" self.assertRaises(ValueError, config._validateStage) config.stage.remotePeers[0].remoteUser = "remote" config.stage.remotePeers[0].rcpCommand = "command" config._validateStage() def testValidate_036(self): """ Test validate on an empty store section. """ config = Config() config.store = StoreConfig() self.assertRaises(ValueError, config._validateStore) def testValidate_037(self): """ Test validate on store section with everything filled in. """ config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdrw-74" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-80" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdrw-80" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "dvd+r" config.store.deviceType = "dvdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "dvd+rw" config.store.deviceType = "dvdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() def testValidate_038(self): """ Test validate on store section missing one each of required fields. """ config = Config() config.store = StoreConfig() config.store.mediaType = "cdr-74" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) config.store = StoreConfig() config.store.sourceDir = "/source" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.deviceType = "cdwriter" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) def testValidate_039(self): """ Test validate on store section missing one each of device type, drive speed and capacity mode and the booleans. """ config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.warnMidnite = True config.store.noEject = True config._validateStore() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True config._validateStore() def testValidate_039a(self): """ Test validate on store section with everything filled in, but mismatch device/media. """ config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-74" config.store.deviceType = "dvdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdrw-74" config.store.deviceType = "dvdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdr-80" config.store.deviceType = "dvdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "cdrw-80" config.store.deviceType = "dvdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "dvd+rw" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) config = Config() config.store = StoreConfig() config.store.sourceDir = "/source" config.store.mediaType = "dvd+r" config.store.deviceType = "cdwriter" config.store.devicePath = "/dev/cdrw" config.store.deviceScsiId = "0,0,0" config.store.driveSpeed = 4 config.store.checkData = True config.store.checkMedia = True config.store.warnMidnite = True config.store.noEject = True self.assertRaises(ValueError, config._validateStore) def testValidate_040(self): """ Test validate on an empty purge section, with a None list. """ config = Config() config.purge = PurgeConfig() config.purge.purgeDirs = None config._validatePurge() def testValidate_041(self): """ Test validate on an empty purge section, with [] for the list. """ config = Config() config.purge = PurgeConfig() config.purge.purgeDirs = [] config._validatePurge() def testValidate_042(self): """ Test validate on an a purge section, with one empty purge dir. """ config = Config() config.purge = PurgeConfig() config.purge.purgeDirs = [ PurgeDir(), ] self.assertRaises(ValueError, config._validatePurge) def testValidate_043(self): """ Test validate on an a purge section, with one purge dir that has only a path. """ config = Config() config.purge = PurgeConfig() config.purge.purgeDirs = [ PurgeDir(absolutePath="/whatever"), ] self.assertRaises(ValueError, config._validatePurge) def testValidate_044(self): """ Test validate on an a purge section, with one purge dir that has only retain days. """ config = Config() config.purge = PurgeConfig() config.purge.purgeDirs = [ PurgeDir(retainDays=3), ] self.assertRaises(ValueError, config._validatePurge) def testValidate_045(self): """ Test validate on an a purge section, with one purge dir that makes sense. """ config = Config() config.purge = PurgeConfig() config.purge.purgeDirs = [ PurgeDir(absolutePath="/whatever", retainDays=4), ] config._validatePurge() def testValidate_046(self): """ Test validate on an a purge section, with several purge dirs that make sense. """ config = Config() config.purge = PurgeConfig() config.purge.purgeDirs = [ PurgeDir("/whatever", 4), PurgeDir("/etc/different", 12), ] config._validatePurge() def testValidate_047(self): """ Test that we catch a duplicate extended action name. """ config = Config() config.extensions = ExtensionsConfig() config.extensions.orderMode = "dependency" config.extensions.actions = [ ExtendedAction("unique1", "b", "c", dependencies=ActionDependencies()), ExtendedAction("unique2", "f", "g", dependencies=ActionDependencies()), ] config._validateExtensions() config.extensions.actions = [ ExtendedAction("duplicate", "b", "c", dependencies=ActionDependencies()), ExtendedAction("duplicate", "f", "g", dependencies=ActionDependencies()), ] self.assertRaises(ValueError, config._validateExtensions) def testValidate_048(self): """ Test that we catch a duplicate local peer name in stage configuration. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), LocalPeer(name="unique2", collectDir="/nowhere"), ] config._validateStage() config.stage.localPeers = [ LocalPeer(name="duplicate", collectDir="/nowhere"), LocalPeer(name="duplicate", collectDir="/nowhere"), ] self.assertRaises(ValueError, config._validateStage) def testValidate_049(self): """ Test that we catch a duplicate remote peer name in stage configuration. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.remotePeers = [ RemotePeer(name="unique1", collectDir="/some/path/to/data"), RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config._validateStage() config.stage.remotePeers = [ RemotePeer(name="duplicate", collectDir="/some/path/to/data"), RemotePeer(name="duplicate", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validateStage) def testValidate_050(self): """ Test that we catch a duplicate peer name duplicated between remote and local in stage configuration. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.stage = StageConfig() config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), ] config.stage.remotePeers = [ RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config._validateStage() config.stage.localPeers = [ LocalPeer(name="duplicate", collectDir="/nowhere"), ] config.stage.remotePeers = [ RemotePeer(name="duplicate", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validateStage) def testValidate_051(self): """ Test validate on a None peers section. """ config = Config() config.peers = None config._validatePeers() def testValidate_052(self): """ Test validate on an empty peers section. """ config = Config() config.peers = PeersConfig() self.assertRaises(ValueError, config._validatePeers) def testValidate_053(self): """ Test validate on peers section containing None for the lists. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = None config.peers.remotePeers = None self.assertRaises(ValueError, config._validatePeers) def testValidate_054(self): """ Test validate on peers section containing [] for the lists. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = [] config.peers.remotePeers = [] self.assertRaises(ValueError, config._validatePeers) def testValidate_055(self): """ Test validate on peers section containing one local peer that is empty. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = [ LocalPeer(), ] self.assertRaises(ValueError, config._validatePeers) def testValidate_056(self): """ Test validate on peers section containing local peer with only a name. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = [ LocalPeer(name="name"), ] self.assertRaises(ValueError, config._validatePeers) def testValidate_057(self): """ Test validate on peers section containing one local peer with a name and path, None for remote list. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = [ LocalPeer(name="name", collectDir="/somewhere"), ] config.peers.remotePeers = None config._validatePeers() def testValidate_058(self): """ Test validate on peers section containing one local peer with a name and path, [] for remote list. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = [ LocalPeer(name="name", collectDir="/somewhere"), ] config.peers.remotePeers = [] config._validatePeers() def testValidate_059(self): """ Test validate on peers section containing one remote peer that is empty. """ config = Config() config.peers = PeersConfig() config.peers.remotePeers = [ RemotePeer(), ] self.assertRaises(ValueError, config._validatePeers) def testValidate_060(self): """ Test validate on peers section containing one remote peer with only a name. """ config = Config() config.peers = PeersConfig() config.peers.remotePeers = [ RemotePeer(name="blech"), ] self.assertRaises(ValueError, config._validatePeers) def testValidate_061(self): """ Test validate on peers section containing one remote peer with a name and path, None for local list. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = None config.peers.remotePeers = [ RemotePeer(name="blech", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validatePeers) config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config._validatePeers() config.options = None self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[-1].remoteUser = "remote" config.peers.remotePeers[-1].rcpCommand = "command" config._validatePeers() def testValidate_062(self): """ Test validate on peers section containing one remote peer with a name and path, [] for local list. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = [] config.peers.remotePeers = [ RemotePeer(name="blech", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validatePeers) config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config._validatePeers() config.options = None self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[-1].remoteUser = "remote" config.peers.remotePeers[-1].rcpCommand = "command" config._validatePeers() def testValidate_063(self): """ Test validate on peers section containing one remote and one local peer. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = [ LocalPeer(name="metoo", collectDir="/nowhere"), ] config.peers.remotePeers = [ RemotePeer(name="blech", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validatePeers) config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config._validatePeers() config.options = None self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[-1].remoteUser = "remote" config.peers.remotePeers[-1].rcpCommand = "command" config._validatePeers() def testValidate_064(self): """ Test validate on peers section containing multiple remote and local peers. """ config = Config() config.peers = PeersConfig() config.peers.localPeers = [ LocalPeer(name="metoo", collectDir="/nowhere"), LocalPeer("one", "/two"), LocalPeer("a", "/b"), ] config.peers.remotePeers = [ RemotePeer(name="blech", collectDir="/some/path/to/data"), RemotePeer("c", "/d"), ] self.assertRaises(ValueError, config._validatePeers) config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config._validatePeers() config.options = None self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[-1].remoteUser = "remote" config.peers.remotePeers[-1].rcpCommand = "command" self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[0].remoteUser = "remote" config.peers.remotePeers[0].rcpCommand = "command" config._validatePeers() def testValidate_065(self): """ Test that we catch a duplicate local peer name in peers configuration. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.peers = PeersConfig() config.peers.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), LocalPeer(name="unique2", collectDir="/nowhere"), ] config._validatePeers() config.peers.localPeers = [ LocalPeer(name="duplicate", collectDir="/nowhere"), LocalPeer(name="duplicate", collectDir="/nowhere"), ] self.assertRaises(ValueError, config._validatePeers) def testValidate_066(self): """ Test that we catch a duplicate remote peer name in peers configuration. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.peers = PeersConfig() config.peers.remotePeers = [ RemotePeer(name="unique1", collectDir="/some/path/to/data"), RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config._validatePeers() config.peers.remotePeers = [ RemotePeer(name="duplicate", collectDir="/some/path/to/data"), RemotePeer(name="duplicate", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validatePeers) def testValidate_067(self): """ Test that we catch a duplicate peer name duplicated between remote and local in peers configuration. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.peers = PeersConfig() config.peers.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), ] config.peers.remotePeers = [ RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config._validatePeers() config.peers.localPeers = [ LocalPeer(name="duplicate", collectDir="/nowhere"), ] config.peers.remotePeers = [ RemotePeer(name="duplicate", collectDir="/some/path/to/data"), ] self.assertRaises(ValueError, config._validatePeers) def testValidate_068(self): """ Test that stage peers can be None, if peers configuration is not None. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.peers = PeersConfig() config.stage = StageConfig() config.peers.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), ] config.peers.remotePeers = [ RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config.stage.targetDir = "/whatever" config.stage.localPeers = None config.stage.remotePeers = None config._validatePeers() config._validateStage() def testValidate_069(self): """ Test that stage peers can be empty lists, if peers configuration is not None. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.peers = PeersConfig() config.stage = StageConfig() config.peers.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), ] config.peers.remotePeers = [ RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config.stage.targetDir = "/whatever" config.stage.localPeers = [] config.stage.remotePeers = [] config._validatePeers() config._validateStage() def testValidate_070(self): """ Test that staging local peers must be valid if filled in, even if peers configuration is not None. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.peers = PeersConfig() config.stage = StageConfig() config.peers.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), ] config.peers.remotePeers = [ RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(), ] # empty local peer is invalid, so validation should catch it config.stage.remotePeers = [] config._validatePeers() self.assertRaises(ValueError, config._validateStage) def testValidate_071(self): """ Test that staging remote peers must be valid if filled in, even if peers configuration is not None. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.peers = PeersConfig() config.stage = StageConfig() config.peers.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), ] config.peers.remotePeers = [ RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config.stage.targetDir = "/whatever" config.stage.localPeers = [] config.stage.remotePeers = [ RemotePeer(), ] # empty remote peer is invalid, so validation should catch it config._validatePeers() self.assertRaises(ValueError, config._validateStage) def testValidate_072(self): """ Test that staging local and remote peers must be valid if filled in, even if peers configuration is not None. """ config = Config() config.options = OptionsConfig(backupUser="ken", rcpCommand="command") config.peers = PeersConfig() config.stage = StageConfig() config.peers.localPeers = [ LocalPeer(name="unique1", collectDir="/nowhere"), ] config.peers.remotePeers = [ RemotePeer(name="unique2", collectDir="/some/path/to/data"), ] config.stage.targetDir = "/whatever" config.stage.localPeers = [ LocalPeer(), ] # empty local peer is invalid, so validation should catch it config.stage.remotePeers = [ RemotePeer(), ] # empty remote peer is invalid, so validation should catch it config._validatePeers() self.assertRaises(ValueError, config._validateStage) def testValidate_073(self): """ Confirm that remote peer is required to have backup user if not set in options. """ config = Config() config.options = OptionsConfig( backupUser="ken", rcpCommand="rcp", rshCommand="rsh", cbackCommand="cback", managedActions=["collect"] ) config.peers = PeersConfig() config.peers.localPeers = [] config.peers.remotePeers = [ RemotePeer(name="remote", collectDir="/path"), ] config._validatePeers() config.options.backupUser = None self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[0].remoteUser = "ken" config._validatePeers() def testValidate_074(self): """ Confirm that remote peer is required to have rcp command if not set in options. """ config = Config() config.options = OptionsConfig( backupUser="ken", rcpCommand="rcp", rshCommand="rsh", cbackCommand="cback", managedActions=["collect"] ) config.peers = PeersConfig() config.peers.localPeers = [] config.peers.remotePeers = [ RemotePeer(name="remote", collectDir="/path"), ] config._validatePeers() config.options.rcpCommand = None self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[0].rcpCommand = "rcp" config._validatePeers() def testValidate_075(self): """ Confirm that remote managed peer is required to have rsh command if not set in options. """ config = Config() config.options = OptionsConfig( backupUser="ken", rcpCommand="rcp", rshCommand="rsh", cbackCommand="cback", managedActions=["collect"] ) config.peers = PeersConfig() config.peers.localPeers = [] config.peers.remotePeers = [ RemotePeer(name="remote", collectDir="/path"), ] config._validatePeers() config.options.rshCommand = None config._validatePeers() config.peers.remotePeers[0].managed = True self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[0].rshCommand = "rsh" config._validatePeers() def testValidate_076(self): """ Confirm that remote managed peer is required to have cback command if not set in options. """ config = Config() config.options = OptionsConfig( backupUser="ken", rcpCommand="rcp", rshCommand="rsh", cbackCommand="cback", managedActions=["collect"] ) config.peers = PeersConfig() config.peers.localPeers = [] config.peers.remotePeers = [ RemotePeer(name="remote", collectDir="/path"), ] config._validatePeers() config.options.cbackCommand = None config._validatePeers() config.peers.remotePeers[0].managed = True self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[0].cbackCommand = "cback" config._validatePeers() def testValidate_077(self): """ Confirm that remote managed peer is required to have managed actions list if not set in options. """ config = Config() config.options = OptionsConfig( backupUser="ken", rcpCommand="rcp", rshCommand="rsh", cbackCommand="cback", managedActions=["collect"] ) config.peers = PeersConfig() config.peers.localPeers = [] config.peers.remotePeers = [ RemotePeer(name="remote", collectDir="/path"), ] config._validatePeers() config.options.managedActions = None config._validatePeers() config.peers.remotePeers[0].managed = True self.assertRaises(ValueError, config._validatePeers) config.options.managedActions = [] self.assertRaises(ValueError, config._validatePeers) config.peers.remotePeers[0].managedActions = [ "collect", ] config._validatePeers() def testValidate_078(self): """ Test case where dereference is True but link depth is None. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir( absolutePath="/stuff", collectMode="incr", archiveMode="tar", ignoreFile="i", linkDepth=None, dereference=True ), ] self.assertRaises(ValueError, config._validateCollect) def testValidate_079(self): """ Test case where dereference is True but link depth is zero. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir(absolutePath="/stuff", collectMode="incr", archiveMode="tar", ignoreFile="i", linkDepth=0, dereference=True), ] self.assertRaises(ValueError, config._validateCollect) def testValidate_080(self): """ Test case where dereference is False and linkDepth is None. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir( absolutePath="/stuff", collectMode="incr", archiveMode="tar", ignoreFile="i", linkDepth=None, dereference=False ), ] config._validateCollect() def testValidate_081(self): """ Test case where dereference is None and linkDepth is None. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir( absolutePath="/stuff", collectMode="incr", archiveMode="tar", ignoreFile="i", linkDepth=None, dereference=None ), ] config._validateCollect() def testValidate_082(self): """ Test case where dereference is False and linkDepth is zero. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir( absolutePath="/stuff", collectMode="incr", archiveMode="tar", ignoreFile="i", linkDepth=0, dereference=False ), ] config._validateCollect() def testValidate_083(self): """ Test case where dereference is None and linkDepth is zero. """ config = Config() config.collect = CollectConfig() config.collect.targetDir = "/whatever" config.collect.collectDirs = [ CollectDir(absolutePath="/stuff", collectMode="incr", archiveMode="tar", ignoreFile="i", linkDepth=0, dereference=None), ] config._validateCollect() ############################ # Test parsing of documents ############################ def testParse_001(self): """ Parse empty config document, validate=False. """ path = self.resources["cback.conf.2"] config = Config(xmlPath=path, validate=False) expected = Config() self.assertEqual(expected, config) def testParse_002(self): """ Parse empty config document, validate=True. """ path = self.resources["cback.conf.2"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_003(self): """ Parse config document containing only a reference section, containing only required fields, validate=False. """ path = self.resources["cback.conf.3"] config = Config(xmlPath=path, validate=False) expected = Config() expected.reference = ReferenceConfig() self.assertEqual(expected, config) def testParse_004(self): """ Parse config document containing only a reference section, containing only required fields, validate=True. """ path = self.resources["cback.conf.3"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_005(self): """ Parse config document containing only a reference section, containing all required and optional fields, validate=False. """ path = self.resources["cback.conf.4"] config = Config(xmlPath=path, validate=False) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration", "Generated by hand.") self.assertEqual(expected, config) def testParse_006(self): """ Parse config document containing only a reference section, containing all required and optional fields, validate=True. """ path = self.resources["cback.conf.4"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_007(self): """ Parse config document containing only a extensions section, containing only required fields, validate=False. """ path = self.resources["cback.conf.16"] config = Config(xmlPath=path, validate=False) expected = Config() expected.extensions = ExtensionsConfig() expected.extensions.actions = [] expected.extensions.actions.append(ExtendedAction("example", "something.whatever", "example", 1)) self.assertEqual(expected, config) def testParse_008(self): """ Parse config document containing only a extensions section, containing only required fields, validate=True. """ path = self.resources["cback.conf.16"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_009(self): """ Parse config document containing only a extensions section, containing all fields, order mode is "index", validate=False. """ path = self.resources["cback.conf.18"] config = Config(xmlPath=path, validate=False) expected = Config() expected.extensions = ExtensionsConfig() expected.extensions.orderMode = "index" expected.extensions.actions = [] expected.extensions.actions.append(ExtendedAction("example", "something.whatever", "example", 1)) self.assertEqual(expected, config) def testParse_009a(self): """ Parse config document containing only a extensions section, containing all fields, order mode is "dependency", validate=False. """ path = self.resources["cback.conf.19"] config = Config(xmlPath=path, validate=False) expected = Config() expected.extensions = ExtensionsConfig() expected.extensions.orderMode = "dependency" expected.extensions.actions = [] expected.extensions.actions.append( ExtendedAction("sysinfo", "CedarBackup3.extend.sysinfo", "executeAction", index=None, dependencies=ActionDependencies()) ) expected.extensions.actions.append( ExtendedAction("mysql", "CedarBackup3.extend.mysql", "executeAction", index=None, dependencies=ActionDependencies()) ) expected.extensions.actions.append( ExtendedAction( "postgresql", "CedarBackup3.extend.postgresql", "executeAction", index=None, dependencies=ActionDependencies(beforeList=["one"]), ) ) expected.extensions.actions.append( ExtendedAction( "subversion", "CedarBackup3.extend.subversion", "executeAction", index=None, dependencies=ActionDependencies(afterList=["one"]), ) ) expected.extensions.actions.append( ExtendedAction( "mbox", "CedarBackup3.extend.mbox", "executeAction", index=None, dependencies=ActionDependencies(beforeList=["one"], afterList=["one"]), ) ) expected.extensions.actions.append( ExtendedAction( "encrypt", "CedarBackup3.extend.encrypt", "executeAction", index=None, dependencies=ActionDependencies( beforeList=["a", "b", "c", "d"], afterList=["one", "two", "three", "four", "five", "six", "seven", "eight"] ), ) ) expected.extensions.actions.append( ExtendedAction( "amazons3", "CedarBackup3.extend.amazons3", "executeAction", index=None, dependencies=ActionDependencies() ) ) self.assertEqual(expected, config) def testParse_010(self): """ Parse config document containing only a extensions section, containing all fields, order mode is "index", validate=True. """ path = self.resources["cback.conf.18"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_010a(self): """ Parse config document containing only a extensions section, containing all fields, order mode is "dependency", validate=True. """ path = self.resources["cback.conf.19"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_011(self): """ Parse config document containing only an options section, containing only required fields, validate=False. """ path = self.resources["cback.conf.5"] config = Config(xmlPath=path, validate=False) expected = Config() expected.options = OptionsConfig("tuesday", "/opt/backup/tmp", "backup", "group", "/usr/bin/scp -1 -B") self.assertEqual(expected, config) def testParse_012(self): """ Parse config document containing only an options section, containing only required fields, validate=True. """ path = self.resources["cback.conf.5"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_013(self): """ Parse config document containing only an options section, containing required and optional fields, validate=False. """ path = self.resources["cback.conf.6"] config = Config(xmlPath=path, validate=False) expected = Config() expected.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "group", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh", "/usr/bin/cback", [] ) expected.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] expected.options.hooks = [ PreActionHook("collect", "ls -l"), PostActionHook("stage", "df -k"), ] expected.options.managedActions = [ "collect", "purge", ] self.assertEqual(expected, config) def testParse_014(self): """ Parse config document containing only an options section, containing required and optional fields, validate=True. """ path = self.resources["cback.conf.6"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_015(self): """ Parse config document containing only a collect section, containing only required fields, validate=False. (Case with single collect directory.) """ path = self.resources["cback.conf.7"] config = Config(xmlPath=path, validate=False) expected = Config() expected.collect = CollectConfig("/opt/backup/collect", "daily", "tar", ".ignore") expected.collect.collectDirs = [ CollectDir(absolutePath="/etc"), ] self.assertEqual(expected, config) def testParse_015a(self): """ Parse config document containing only a collect section, containing only required fields, validate=False. (Case with single collect file.) """ path = self.resources["cback.conf.17"] config = Config(xmlPath=path, validate=False) expected = Config() expected.collect = CollectConfig("/opt/backup/collect", "daily", "tar", ".ignore") expected.collect.collectFiles = [ CollectFile(absolutePath="/etc"), ] self.assertEqual(expected, config) def testParse_016(self): """ Parse config document containing only a collect section, containing only required fields, validate=True. (Case with single collect directory.) """ path = self.resources["cback.conf.7"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_016a(self): """ Parse config document containing only a collect section, containing only required fields, validate=True. (Case with single collect file.) """ path = self.resources["cback.conf.17"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_017(self): """ Parse config document containing only a collect section, containing required and optional fields, validate=False. """ path = self.resources["cback.conf.8"] config = Config(xmlPath=path, validate=False) expected = Config() expected.collect = CollectConfig("/opt/backup/collect", "daily", "targz", ".cbignore") expected.collect.absoluteExcludePaths = [ "/etc/cback.conf", "/etc/X11", ] expected.collect.excludePatterns = [ ".*tmp.*", r".*\.netscape\/.*", ] expected.collect.collectFiles = [] expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.profile")) expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.kshrc", collectMode="weekly")) expected.collect.collectFiles.append( CollectFile(absolutePath="/home/root/.aliases", collectMode="daily", archiveMode="tarbz2") ) expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir(absolutePath="/root", recursionLevel=1)) expected.collect.collectDirs.append(CollectDir(absolutePath="/tmp", linkDepth=3)) expected.collect.collectDirs.append(CollectDir(absolutePath="/ken", linkDepth=1, dereference=True)) expected.collect.collectDirs.append(CollectDir(absolutePath="/var/log", collectMode="incr")) expected.collect.collectDirs.append( CollectDir(absolutePath="/etc", collectMode="incr", archiveMode="tar", ignoreFile=".ignore") ) collectDir = CollectDir(absolutePath="/opt") collectDir.absoluteExcludePaths = [ "/opt/share", "/opt/tmp", ] collectDir.relativeExcludePaths = [ "large", "backup", ] collectDir.excludePatterns = [ r".*\.doc\.*", r".*\.xls\.*", ] expected.collect.collectDirs.append(collectDir) self.assertEqual(expected, config) def testParse_018(self): """ Parse config document containing only a collect section, containing required and optional fields, validate=True. """ path = self.resources["cback.conf.8"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_019(self): """ Parse config document containing only a stage section, containing only required fields, validate=False. """ path = self.resources["cback.conf.9"] config = Config(xmlPath=path, validate=False) expected = Config() expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = None expected.stage.remotePeers = [ RemotePeer("machine2", "/opt/backup/collect"), ] self.assertEqual(expected, config) def testParse_020(self): """ Parse config document containing only a stage section, containing only required fields, validate=True. """ path = self.resources["cback.conf.9"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_021(self): """ Parse config document containing only a stage section, containing all required and optional fields, validate=False. """ path = self.resources["cback.conf.10"] config = Config(xmlPath=path, validate=False) expected = Config() expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = [] expected.stage.remotePeers = [] expected.stage.localPeers.append(LocalPeer("machine1-1", "/opt/backup/collect")) expected.stage.localPeers.append(LocalPeer("machine1-2", "/var/backup")) expected.stage.remotePeers.append(RemotePeer("machine2", "/backup/collect", ignoreFailureMode="all")) expected.stage.remotePeers.append(RemotePeer("machine3", "/home/whatever/tmp", remoteUser="someone", rcpCommand="scp -B")) self.assertEqual(expected, config) def testParse_022(self): """ Parse config document containing only a stage section, containing all required and optional fields, validate=True. """ path = self.resources["cback.conf.10"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_023(self): """ Parse config document containing only a store section, containing only required fields, validate=False. """ path = self.resources["cback.conf.11"] config = Config(xmlPath=path, validate=False) expected = Config() expected.store = StoreConfig("/opt/backup/staging", mediaType="cdrw-74", devicePath="/dev/cdrw", deviceScsiId=None) self.assertEqual(expected, config) def testParse_024(self): """ Parse config document containing only a store section, containing only required fields, validate=True. """ path = self.resources["cback.conf.11"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_025(self): """ Parse config document containing only a store section, containing all required and optional fields, validate=False. """ path = self.resources["cback.conf.12"] config = Config(xmlPath=path, validate=False) expected = Config() expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.mediaType = "cdrw-74" expected.store.deviceType = "cdwriter" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = "0,0,0" expected.store.driveSpeed = 4 expected.store.checkData = True expected.store.checkMedia = True expected.store.warnMidnite = True expected.store.noEject = True expected.store.refreshMediaDelay = 12 expected.store.ejectDelay = 13 expected.store.blankBehavior = BlankBehavior() expected.store.blankBehavior.blankMode = "weekly" expected.store.blankBehavior.blankFactor = "1.3" self.assertEqual(expected, config) def testParse_026(self): """ Parse config document containing only a store section, containing all required and optional fields, validate=True. """ path = self.resources["cback.conf.12"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_027(self): """ Parse config document containing only a purge section, containing only required fields, validate=False. """ path = self.resources["cback.conf.13"] config = Config(xmlPath=path, validate=False) expected = Config() expected.purge = PurgeConfig() expected.purge.purgeDirs = [ PurgeDir("/opt/backup/stage", 5), ] self.assertEqual(expected, config) def testParse_028(self): """ Parse config document containing only a purge section, containing only required fields, validate=True. """ path = self.resources["cback.conf.13"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_029(self): """ Parse config document containing only a purge section, containing all required and optional fields, validate=False. """ path = self.resources["cback.conf.14"] config = Config(xmlPath=path, validate=False) expected = Config() expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) expected.purge.purgeDirs.append(PurgeDir("/home/backup/tmp", 12)) self.assertEqual(expected, config) def testParse_030(self): """ Parse config document containing only a purge section, containing all required and optional fields, validate=True. """ path = self.resources["cback.conf.14"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_031(self): """ Parse complete document containing all required and optional fields, "index" extensions, validate=False. """ path = self.resources["cback.conf.15"] config = Config(xmlPath=path, validate=False) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration", "Generated by hand.") expected.extensions = ExtensionsConfig() expected.extensions.orderMode = "index" expected.extensions.actions = [] expected.extensions.actions.append(ExtendedAction("example", "something.whatever", "example", 102)) expected.extensions.actions.append(ExtendedAction("bogus", "module", "something", 350)) expected.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "group", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh", "/usr/bin/cback", [] ) expected.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] expected.options.hooks = [ PreActionHook("collect", "ls -l"), PreActionHook("subversion", 'mailx -S "hello"'), PostActionHook("stage", "df -k"), ] expected.options.managedActions = [ "collect", "purge", ] expected.collect = CollectConfig("/opt/backup/collect", "daily", "targz", ".cbignore") expected.collect.absoluteExcludePaths = [ "/etc/cback.conf", "/etc/X11", ] expected.collect.excludePatterns = [ ".*tmp.*", r".*\.netscape\/.*", ] expected.collect.collectFiles = [] expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.profile")) expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.kshrc", collectMode="weekly")) expected.collect.collectFiles.append( CollectFile(absolutePath="/home/root/.aliases", collectMode="daily", archiveMode="tarbz2") ) expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir(absolutePath="/root")) expected.collect.collectDirs.append(CollectDir(absolutePath="/tmp", linkDepth=3)) expected.collect.collectDirs.append(CollectDir(absolutePath="/ken", linkDepth=1, dereference=True)) expected.collect.collectDirs.append(CollectDir(absolutePath="/var/log", collectMode="incr")) expected.collect.collectDirs.append( CollectDir(absolutePath="/etc", collectMode="incr", archiveMode="tar", ignoreFile=".ignore") ) collectDir = CollectDir(absolutePath="/opt") collectDir.absoluteExcludePaths = [ "/opt/share", "/opt/tmp", ] collectDir.relativeExcludePaths = [ "large", "backup", ] collectDir.excludePatterns = [ r".*\.doc\.*", r".*\.xls\.*", ] expected.collect.collectDirs.append(collectDir) expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = [] expected.stage.remotePeers = [] expected.stage.localPeers.append(LocalPeer("machine1-1", "/opt/backup/collect")) expected.stage.localPeers.append(LocalPeer("machine1-2", "/var/backup")) expected.stage.remotePeers.append(RemotePeer("machine2", "/backup/collect", ignoreFailureMode="all")) expected.stage.remotePeers.append(RemotePeer("machine3", "/home/whatever/tmp", remoteUser="someone", rcpCommand="scp -B")) expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.mediaType = "cdrw-74" expected.store.deviceType = "cdwriter" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = None expected.store.driveSpeed = 4 expected.store.checkData = True expected.store.checkMedia = True expected.store.warnMidnite = True expected.store.noEject = True expected.store.blankBehavior = BlankBehavior() expected.store.blankBehavior.blankMode = "weekly" expected.store.blankBehavior.blankFactor = "1.3" expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) expected.purge.purgeDirs.append(PurgeDir("/home/backup/tmp", 12)) self.assertEqual(expected, config) def testParse_031a(self): """ Parse complete document containing all required and optional fields, "dependency" extensions, validate=False. """ path = self.resources["cback.conf.20"] config = Config(xmlPath=path, validate=False) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration", "Generated by hand.") expected.extensions = ExtensionsConfig() expected.extensions.orderMode = "dependency" expected.extensions.actions = [] expected.extensions.actions.append( ExtendedAction("example", "something.whatever", "example", index=None, dependencies=ActionDependencies()) ) expected.extensions.actions.append( ExtendedAction( "bogus", "module", "something", index=None, dependencies=ActionDependencies(beforeList=["a", "b", "c"], afterList=["one"]), ) ) expected.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "group", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh", "/usr/bin/cback", [] ) expected.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] expected.options.hooks = [ PreActionHook("collect", "ls -l"), PreActionHook("subversion", 'mailx -S "hello"'), PostActionHook("stage", "df -k"), ] expected.options.managedActions = [ "collect", "purge", ] expected.collect = CollectConfig("/opt/backup/collect", "daily", "targz", ".cbignore") expected.collect.absoluteExcludePaths = [ "/etc/cback.conf", "/etc/X11", ] expected.collect.excludePatterns = [ ".*tmp.*", r".*\.netscape\/.*", ] expected.collect.collectFiles = [] expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.profile")) expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.kshrc", collectMode="weekly")) expected.collect.collectFiles.append( CollectFile(absolutePath="/home/root/.aliases", collectMode="daily", archiveMode="tarbz2") ) expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir(absolutePath="/root")) expected.collect.collectDirs.append(CollectDir(absolutePath="/tmp", linkDepth=3)) expected.collect.collectDirs.append(CollectDir(absolutePath="/ken", linkDepth=1, dereference=True)) expected.collect.collectDirs.append(CollectDir(absolutePath="/var/log", collectMode="incr")) expected.collect.collectDirs.append( CollectDir(absolutePath="/etc", collectMode="incr", archiveMode="tar", ignoreFile=".ignore") ) collectDir = CollectDir(absolutePath="/opt") collectDir.absoluteExcludePaths = [ "/opt/share", "/opt/tmp", ] collectDir.relativeExcludePaths = [ "large", "backup", ] collectDir.excludePatterns = [ r".*\.doc\.*", r".*\.xls\.*", ] expected.collect.collectDirs.append(collectDir) expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = [] expected.stage.remotePeers = [] expected.stage.localPeers.append(LocalPeer("machine1-1", "/opt/backup/collect")) expected.stage.localPeers.append(LocalPeer("machine1-2", "/var/backup")) expected.stage.remotePeers.append(RemotePeer("machine2", "/backup/collect", ignoreFailureMode="all")) expected.stage.remotePeers.append(RemotePeer("machine3", "/home/whatever/tmp", remoteUser="someone", rcpCommand="scp -B")) expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.mediaType = "dvd+rw" expected.store.deviceType = "dvdwriter" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = None expected.store.driveSpeed = 1 expected.store.checkData = True expected.store.checkMedia = True expected.store.warnMidnite = True expected.store.noEject = True expected.store.blankBehavior = BlankBehavior() expected.store.blankBehavior.blankMode = "weekly" expected.store.blankBehavior.blankFactor = "1.3" expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) expected.purge.purgeDirs.append(PurgeDir("/home/backup/tmp", 12)) self.assertEqual(expected, config) def testParse_032(self): """ Parse complete document containing all required and optional fields, "index" extensions, validate=True. """ path = self.resources["cback.conf.15"] config = Config(xmlPath=path, validate=True) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration", "Generated by hand.") expected.extensions = ExtensionsConfig() expected.extensions.orderMode = "index" expected.extensions.actions = [] expected.extensions.actions.append(ExtendedAction("example", "something.whatever", "example", 102)) expected.extensions.actions.append(ExtendedAction("bogus", "module", "something", 350)) expected.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "group", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh", "/usr/bin/cback", [] ) expected.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] expected.options.hooks = [ PreActionHook("collect", "ls -l"), PreActionHook("subversion", 'mailx -S "hello"'), PostActionHook("stage", "df -k"), ] expected.options.managedActions = [ "collect", "purge", ] expected.collect = CollectConfig("/opt/backup/collect", "daily", "targz", ".cbignore") expected.collect.absoluteExcludePaths = [ "/etc/cback.conf", "/etc/X11", ] expected.collect.excludePatterns = [ ".*tmp.*", r".*\.netscape\/.*", ] expected.collect.collectFiles = [] expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.profile")) expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.kshrc", collectMode="weekly")) expected.collect.collectFiles.append( CollectFile(absolutePath="/home/root/.aliases", collectMode="daily", archiveMode="tarbz2") ) expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir(absolutePath="/root")) expected.collect.collectDirs.append(CollectDir(absolutePath="/tmp", linkDepth=3)) expected.collect.collectDirs.append(CollectDir(absolutePath="/ken", linkDepth=1, dereference=True)) expected.collect.collectDirs.append(CollectDir(absolutePath="/var/log", collectMode="incr")) expected.collect.collectDirs.append( CollectDir(absolutePath="/etc", collectMode="incr", archiveMode="tar", ignoreFile=".ignore") ) collectDir = CollectDir(absolutePath="/opt") collectDir.absoluteExcludePaths = [ "/opt/share", "/opt/tmp", ] collectDir.relativeExcludePaths = [ "large", "backup", ] collectDir.excludePatterns = [ r".*\.doc\.*", r".*\.xls\.*", ] expected.collect.collectDirs.append(collectDir) expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = [] expected.stage.remotePeers = [] expected.stage.localPeers.append(LocalPeer("machine1-1", "/opt/backup/collect")) expected.stage.localPeers.append(LocalPeer("machine1-2", "/var/backup")) expected.stage.remotePeers.append(RemotePeer("machine2", "/backup/collect", ignoreFailureMode="all")) expected.stage.remotePeers.append(RemotePeer("machine3", "/home/whatever/tmp", remoteUser="someone", rcpCommand="scp -B")) expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.mediaType = "cdrw-74" expected.store.deviceType = "cdwriter" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = None expected.store.driveSpeed = 4 expected.store.checkData = True expected.store.checkMedia = True expected.store.warnMidnite = True expected.store.noEject = True expected.store.blankBehavior = BlankBehavior() expected.store.blankBehavior.blankMode = "weekly" expected.store.blankBehavior.blankFactor = "1.3" expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) expected.purge.purgeDirs.append(PurgeDir("/home/backup/tmp", 12)) self.assertEqual(expected, config) def testParse_032a(self): """ Parse complete document containing all required and optional fields, "dependency" extensions, validate=True. """ path = self.resources["cback.conf.20"] config = Config(xmlPath=path, validate=True) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration", "Generated by hand.") expected.extensions = ExtensionsConfig() expected.extensions.orderMode = "dependency" expected.extensions.actions = [] expected.extensions.actions.append( ExtendedAction("example", "something.whatever", "example", index=None, dependencies=ActionDependencies()) ) expected.extensions.actions.append( ExtendedAction( "bogus", "module", "something", index=None, dependencies=ActionDependencies(beforeList=["a", "b", "c"], afterList=["one"]), ) ) expected.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "group", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh", "/usr/bin/cback", [] ) expected.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] expected.options.hooks = [ PreActionHook("collect", "ls -l"), PreActionHook("subversion", 'mailx -S "hello"'), PostActionHook("stage", "df -k"), ] expected.options.managedActions = [ "collect", "purge", ] expected.collect = CollectConfig("/opt/backup/collect", "daily", "targz", ".cbignore") expected.collect.absoluteExcludePaths = [ "/etc/cback.conf", "/etc/X11", ] expected.collect.excludePatterns = [ ".*tmp.*", r".*\.netscape\/.*", ] expected.collect.collectFiles = [] expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.profile")) expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.kshrc", collectMode="weekly")) expected.collect.collectFiles.append( CollectFile(absolutePath="/home/root/.aliases", collectMode="daily", archiveMode="tarbz2") ) expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir(absolutePath="/root")) expected.collect.collectDirs.append(CollectDir(absolutePath="/tmp", linkDepth=3)) expected.collect.collectDirs.append(CollectDir(absolutePath="/ken", linkDepth=1, dereference=True)) expected.collect.collectDirs.append(CollectDir(absolutePath="/var/log", collectMode="incr")) expected.collect.collectDirs.append( CollectDir(absolutePath="/etc", collectMode="incr", archiveMode="tar", ignoreFile=".ignore") ) collectDir = CollectDir(absolutePath="/opt") collectDir.absoluteExcludePaths = [ "/opt/share", "/opt/tmp", ] collectDir.relativeExcludePaths = [ "large", "backup", ] collectDir.excludePatterns = [ r".*\.doc\.*", r".*\.xls\.*", ] expected.collect.collectDirs.append(collectDir) expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = [] expected.stage.remotePeers = [] expected.stage.localPeers.append(LocalPeer("machine1-1", "/opt/backup/collect")) expected.stage.localPeers.append(LocalPeer("machine1-2", "/var/backup")) expected.stage.remotePeers.append(RemotePeer("machine2", "/backup/collect", ignoreFailureMode="all")) expected.stage.remotePeers.append(RemotePeer("machine3", "/home/whatever/tmp", remoteUser="someone", rcpCommand="scp -B")) expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.mediaType = "dvd+rw" expected.store.deviceType = "dvdwriter" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = None expected.store.driveSpeed = 1 expected.store.checkData = True expected.store.checkMedia = True expected.store.warnMidnite = True expected.store.noEject = True expected.store.blankBehavior = BlankBehavior() expected.store.blankBehavior.blankMode = "weekly" expected.store.blankBehavior.blankFactor = "1.3" expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) expected.purge.purgeDirs.append(PurgeDir("/home/backup/tmp", 12)) self.assertEqual(expected, config) def testParse_033(self): """ Parse a sample from Cedar Backup v1.x, which must still be valid, validate=False. """ path = self.resources["cback.conf.1"] config = Config(xmlPath=path, validate=False) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration") expected.options = OptionsConfig("tuesday", "/opt/backup/tmp", "backup", "backup", "/usr/bin/scp -1 -B") expected.collect = CollectConfig() expected.collect.targetDir = "/opt/backup/collect" expected.collect.archiveMode = "targz" expected.collect.ignoreFile = ".cbignore" expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir("/etc", collectMode="daily")) expected.collect.collectDirs.append(CollectDir("/var/log", collectMode="incr")) collectDir = CollectDir("/opt", collectMode="weekly") collectDir.absoluteExcludePaths = [ "/opt/large", "/opt/backup", "/opt/tmp", ] expected.collect.collectDirs.append(collectDir) expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = [ LocalPeer("machine1", "/opt/backup/collect"), ] expected.stage.remotePeers = [ RemotePeer("machine2", "/opt/backup/collect", remoteUser="backup"), ] expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = "0,0,0" expected.store.driveSpeed = 4 expected.store.mediaType = "cdrw-74" expected.store.checkData = True expected.store.checkMedia = False expected.store.warnMidnite = False expected.store.noEject = False expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) self.assertEqual(expected, config) def testParse_034(self): """ Parse a sample from Cedar Backup v1.x, which must still be valid, validate=True. """ path = self.resources["cback.conf.1"] config = Config(xmlPath=path, validate=True) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration") expected.options = OptionsConfig("tuesday", "/opt/backup/tmp", "backup", "backup", "/usr/bin/scp -1 -B") expected.collect = CollectConfig() expected.collect.targetDir = "/opt/backup/collect" expected.collect.archiveMode = "targz" expected.collect.ignoreFile = ".cbignore" expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir("/etc", collectMode="daily")) expected.collect.collectDirs.append(CollectDir("/var/log", collectMode="incr")) collectDir = CollectDir("/opt", collectMode="weekly") collectDir.absoluteExcludePaths = [ "/opt/large", "/opt/backup", "/opt/tmp", ] expected.collect.collectDirs.append(collectDir) expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = [ LocalPeer("machine1", "/opt/backup/collect"), ] expected.stage.remotePeers = [ RemotePeer("machine2", "/opt/backup/collect", remoteUser="backup"), ] expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = "0,0,0" expected.store.driveSpeed = 4 expected.store.mediaType = "cdrw-74" expected.store.checkData = True expected.store.checkMedia = False expected.store.warnMidnite = False expected.store.noEject = False expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) self.assertEqual(expected, config) def testParse_035(self): """ Document containing all required fields, peers in peer configuration and not staging, validate=False. """ path = self.resources["cback.conf.21"] config = Config(xmlPath=path, validate=False) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration", "Generated by hand.") expected.extensions = ExtensionsConfig() expected.extensions.orderMode = "dependency" expected.extensions.actions = [] expected.extensions.actions.append( ExtendedAction("example", "something.whatever", "example", index=None, dependencies=ActionDependencies()) ) expected.extensions.actions.append( ExtendedAction( "bogus", "module", "something", index=None, dependencies=ActionDependencies(beforeList=["a", "b", "c"], afterList=["one"]), ) ) expected.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "group", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh", "/usr/bin/cback", [] ) expected.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] expected.options.hooks = [ PreActionHook("collect", "ls -l"), PreActionHook("subversion", 'mailx -S "hello"'), PostActionHook("stage", "df -k"), ] expected.options.managedActions = [ "collect", "purge", ] expected.peers = PeersConfig() expected.peers.localPeers = [] expected.peers.remotePeers = [] expected.peers.localPeers.append(LocalPeer("machine1-1", "/opt/backup/collect")) expected.peers.localPeers.append(LocalPeer("machine1-2", "/var/backup")) expected.peers.remotePeers.append(RemotePeer("machine2", "/backup/collect", ignoreFailureMode="all")) expected.peers.remotePeers.append(RemotePeer("machine3", "/home/whatever/tmp", remoteUser="someone", rcpCommand="scp -B")) expected.peers.remotePeers.append( RemotePeer( "machine4", "/aa", remoteUser="someone", rcpCommand="scp -B", rshCommand="ssh", cbackCommand="cback", managed=True, managedActions=None, ) ) expected.peers.remotePeers.append(RemotePeer("machine5", "/bb", managed=False, managedActions=["collect", "purge"])) expected.collect = CollectConfig("/opt/backup/collect", "daily", "targz", ".cbignore") expected.collect.absoluteExcludePaths = [ "/etc/cback.conf", "/etc/X11", ] expected.collect.excludePatterns = [ ".*tmp.*", r".*\.netscape\/.*", ] expected.collect.collectFiles = [] expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.profile")) expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.kshrc", collectMode="weekly")) expected.collect.collectFiles.append( CollectFile(absolutePath="/home/root/.aliases", collectMode="daily", archiveMode="tarbz2") ) expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir(absolutePath="/root")) expected.collect.collectDirs.append(CollectDir(absolutePath="/tmp", linkDepth=3)) expected.collect.collectDirs.append(CollectDir(absolutePath="/ken", linkDepth=1, dereference=True)) expected.collect.collectDirs.append(CollectDir(absolutePath="/var/log", collectMode="incr")) expected.collect.collectDirs.append( CollectDir(absolutePath="/etc", collectMode="incr", archiveMode="tar", ignoreFile=".ignore") ) collectDir = CollectDir(absolutePath="/opt") collectDir.absoluteExcludePaths = [ "/opt/share", "/opt/tmp", ] collectDir.relativeExcludePaths = [ "large", "backup", ] collectDir.excludePatterns = [ r".*\.doc\.*", r".*\.xls\.*", ] expected.collect.collectDirs.append(collectDir) expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = None expected.stage.remotePeers = None expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.mediaType = "dvd+rw" expected.store.deviceType = "dvdwriter" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = None expected.store.driveSpeed = 1 expected.store.checkData = True expected.store.checkMedia = True expected.store.warnMidnite = True expected.store.noEject = True expected.store.blankBehavior = BlankBehavior() expected.store.blankBehavior.blankMode = "weekly" expected.store.blankBehavior.blankFactor = "1.3" expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) expected.purge.purgeDirs.append(PurgeDir("/home/backup/tmp", 12)) self.assertEqual(expected, config) def testParse_036(self): """ Document containing all required fields, peers in peer configuration and not staging, validate=True. """ path = self.resources["cback.conf.21"] config = Config(xmlPath=path, validate=True) expected = Config() expected.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration", "Generated by hand.") expected.extensions = ExtensionsConfig() expected.extensions.orderMode = "dependency" expected.extensions.actions = [] expected.extensions.actions.append( ExtendedAction("example", "something.whatever", "example", index=None, dependencies=ActionDependencies()) ) expected.extensions.actions.append( ExtendedAction( "bogus", "module", "something", index=None, dependencies=ActionDependencies(beforeList=["a", "b", "c"], afterList=["one"]), ) ) expected.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "group", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh", "/usr/bin/cback", [] ) expected.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] expected.options.hooks = [ PreActionHook("collect", "ls -l"), PreActionHook("subversion", 'mailx -S "hello"'), PostActionHook("stage", "df -k"), ] expected.options.managedActions = [ "collect", "purge", ] expected.peers = PeersConfig() expected.peers.localPeers = [] expected.peers.remotePeers = [] expected.peers.localPeers.append(LocalPeer("machine1-1", "/opt/backup/collect")) expected.peers.localPeers.append(LocalPeer("machine1-2", "/var/backup")) expected.peers.remotePeers.append(RemotePeer("machine2", "/backup/collect", ignoreFailureMode="all")) expected.peers.remotePeers.append(RemotePeer("machine3", "/home/whatever/tmp", remoteUser="someone", rcpCommand="scp -B")) expected.peers.remotePeers.append( RemotePeer( "machine4", "/aa", remoteUser="someone", rcpCommand="scp -B", rshCommand="ssh", cbackCommand="cback", managed=True, managedActions=None, ) ) expected.peers.remotePeers.append(RemotePeer("machine5", "/bb", managed=False, managedActions=["collect", "purge"])) expected.collect = CollectConfig("/opt/backup/collect", "daily", "targz", ".cbignore") expected.collect.absoluteExcludePaths = [ "/etc/cback.conf", "/etc/X11", ] expected.collect.excludePatterns = [ ".*tmp.*", r".*\.netscape\/.*", ] expected.collect.collectFiles = [] expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.profile")) expected.collect.collectFiles.append(CollectFile(absolutePath="/home/root/.kshrc", collectMode="weekly")) expected.collect.collectFiles.append( CollectFile(absolutePath="/home/root/.aliases", collectMode="daily", archiveMode="tarbz2") ) expected.collect.collectDirs = [] expected.collect.collectDirs.append(CollectDir(absolutePath="/root")) expected.collect.collectDirs.append(CollectDir(absolutePath="/tmp", linkDepth=3)) expected.collect.collectDirs.append(CollectDir(absolutePath="/ken", linkDepth=1, dereference=True)) expected.collect.collectDirs.append(CollectDir(absolutePath="/var/log", collectMode="incr")) expected.collect.collectDirs.append( CollectDir(absolutePath="/etc", collectMode="incr", archiveMode="tar", ignoreFile=".ignore") ) collectDir = CollectDir(absolutePath="/opt") collectDir.absoluteExcludePaths = [ "/opt/share", "/opt/tmp", ] collectDir.relativeExcludePaths = [ "large", "backup", ] collectDir.excludePatterns = [ r".*\.doc\.*", r".*\.xls\.*", ] expected.collect.collectDirs.append(collectDir) expected.stage = StageConfig() expected.stage.targetDir = "/opt/backup/staging" expected.stage.localPeers = None expected.stage.remotePeers = None expected.store = StoreConfig() expected.store.sourceDir = "/opt/backup/staging" expected.store.mediaType = "dvd+rw" expected.store.deviceType = "dvdwriter" expected.store.devicePath = "/dev/cdrw" expected.store.deviceScsiId = None expected.store.driveSpeed = 1 expected.store.checkData = True expected.store.checkMedia = True expected.store.warnMidnite = True expected.store.noEject = True expected.store.blankBehavior = BlankBehavior() expected.store.blankBehavior.blankMode = "weekly" expected.store.blankBehavior.blankFactor = "1.3" expected.purge = PurgeConfig() expected.purge.purgeDirs = [] expected.purge.purgeDirs.append(PurgeDir("/opt/backup/stage", 5)) expected.purge.purgeDirs.append(PurgeDir("/opt/backup/collect", 0)) expected.purge.purgeDirs.append(PurgeDir("/home/backup/tmp", 12)) self.assertEqual(expected, config) def testParse_037(self): """ Parse config document containing only a peers section, containing only required fields, validate=False. """ path = self.resources["cback.conf.22"] config = Config(xmlPath=path, validate=False) expected = Config() expected.peers = PeersConfig() expected.peers.localPeers = None expected.peers.remotePeers = [ RemotePeer("machine2", "/opt/backup/collect"), ] self.assertEqual(expected, config) def testParse_038(self): """ Parse config document containing only a peers section, containing only required fields, validate=True. """ path = self.resources["cback.conf.9"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) def testParse_039(self): """ Parse config document containing only a peers section, containing all required and optional fields, validate=False. """ path = self.resources["cback.conf.23"] config = Config(xmlPath=path, validate=False) expected = Config() expected.peers = PeersConfig() expected.peers.localPeers = [] expected.peers.remotePeers = [] expected.peers.localPeers.append(LocalPeer("machine1-1", "/opt/backup/collect")) expected.peers.localPeers.append(LocalPeer("machine1-2", "/var/backup")) expected.peers.remotePeers.append(RemotePeer("machine2", "/backup/collect", ignoreFailureMode="all")) expected.peers.remotePeers.append(RemotePeer("machine3", "/home/whatever/tmp", remoteUser="someone", rcpCommand="scp -B")) expected.peers.remotePeers.append( RemotePeer( "machine4", "/aa", remoteUser="someone", rcpCommand="scp -B", rshCommand="ssh", cbackCommand="cback", managed=True, managedActions=None, ) ) expected.peers.remotePeers.append(RemotePeer("machine5", "/bb", managed=False, managedActions=["collect", "purge"])) self.assertEqual(expected, config) def testParse_040(self): """ Parse config document containing only a peers section, containing all required and optional fields, validate=True. """ path = self.resources["cback.conf.23"] self.assertRaises(ValueError, Config, xmlPath=path, validate=True) ######################### # Test the extract logic ######################### def testExtractXml_001(self): """ Extract empty config document, validate=True. """ before = Config() self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_002(self): """ Extract empty config document, validate=False. """ before = Config() beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_003(self): """ Extract document containing only a valid reference section, validate=True. """ before = Config() before.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration") self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_004(self): """ Extract document containing only a valid reference section, validate=False. """ before = Config() before.reference = ReferenceConfig("$Author: pronovic $", "1.3", "Sample configuration") beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_005(self): """ Extract document containing only a valid extensions section, empty list, orderMode=None, validate=True. """ before = Config() before.extensions = ExtensionsConfig() before.extensions.orderMode = None before.extensions.actions = [] self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_006(self): """ Extract document containing only a valid extensions section, non-empty list and orderMode="index", validate=True. """ before = Config() before.extensions = ExtensionsConfig() before.extensions.orderMode = "index" before.extensions.actions = [] before.extensions.actions.append(ExtendedAction("name", "module", "function", 1)) self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_006a(self): """ Extract document containing only a valid extensions section, non-empty list and orderMode="dependency", validate=True. """ before = Config() before.extensions = ExtensionsConfig() before.extensions.orderMode = "dependency" before.extensions.actions = [] before.extensions.actions.append( ExtendedAction("name", "module", "function", dependencies=ActionDependencies(beforeList=["b"], afterList=["a"])) ) self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_007(self): """ Extract document containing only a valid extensions section, empty list, orderMode=None, validate=False. """ before = Config() before.extensions = ExtensionsConfig() beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_008(self): """ Extract document containing only a valid extensions section, orderMode="index", validate=False. """ before = Config() before.extensions = ExtensionsConfig() before.extensions.orderMode = "index" before.extensions.actions = [] before.extensions.actions.append(ExtendedAction("name", "module", "function", 1)) beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_009(self): """ Extract document containing only an invalid extensions section, validate=True. """ before = Config() before.extensions = ExtensionsConfig() before.extensions.actions = [] before.extensions.actions.append(ExtendedAction("name", "module", None, None)) self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_010(self): """ Extract document containing only an invalid extensions section, validate=False. """ before = Config() before.extensions = ExtensionsConfig() before.extensions.actions = [] before.extensions.actions.append(ExtendedAction("name", "module", None, None)) beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_011(self): """ Extract document containing only a valid options section, validate=True. """ before = Config() before.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "backup", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh" ) before.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] before.options.hooks = [ PostActionHook("collect", "ls -l"), ] self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_012(self): """ Extract document containing only a valid options section, validate=False. """ before = Config() before.options = OptionsConfig( "tuesday", "/opt/backup/tmp", "backup", "backup", "/usr/bin/scp -1 -B", [], [], "/usr/bin/ssh" ) before.options.overrides = [ CommandOverride("mkisofs", "/usr/bin/mkisofs"), CommandOverride("svnlook", "/svnlook"), ] before.options.hooks = [ PostActionHook("collect", "ls -l"), ] beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_013(self): """ Extract document containing only an invalid options section, validate=True. """ before = Config() before.options = OptionsConfig() self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_014(self): """ Extract document containing only an invalid options section, validate=False. """ before = Config() before.options = OptionsConfig() beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_015(self): """ Extract document containing only a valid collect section, empty lists, validate=True. (Test a directory.) """ before = Config() before.collect = CollectConfig() before.collect.targetDir = "/opt/backup/collect" before.collect.archiveMode = "targz" before.collect.ignoreFile = ".cbignore" before.collect.collectDirs = [ CollectDir("/etc", collectMode="daily"), ] self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_015a(self): """ Extract document containing only a valid collect section, empty lists, validate=True. (Test a file.) """ before = Config() before.collect = CollectConfig() before.collect.targetDir = "/opt/backup/collect" before.collect.archiveMode = "targz" before.collect.ignoreFile = ".cbignore" before.collect.collectFiles = [ CollectFile("/etc", collectMode="daily"), ] self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_016(self): """ Extract document containing only a valid collect section, empty lists, validate=False. (Test a directory.) """ before = Config() before.collect = CollectConfig() before.collect.targetDir = "/opt/backup/collect" before.collect.archiveMode = "targz" before.collect.ignoreFile = ".cbignore" before.collect.collectDirs = [ CollectDir("/etc", collectMode="daily"), ] beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_016a(self): """ Extract document containing only a valid collect section, empty lists, validate=False. (Test a file.) """ before = Config() before.collect = CollectConfig() before.collect.targetDir = "/opt/backup/collect" before.collect.archiveMode = "targz" before.collect.ignoreFile = ".cbignore" before.collect.collectFiles = [ CollectFile("/etc", collectMode="daily"), ] beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_017(self): """ Extract document containing only a valid collect section, non-empty lists, validate=True. (Test a directory.) """ before = Config() before.collect = CollectConfig() before.collect.targetDir = "/opt/backup/collect" before.collect.archiveMode = "targz" before.collect.ignoreFile = ".cbignore" before.collect.absoluteExcludePaths = [ "/one", "/two", "/three", ] before.collect.excludePatterns = [ "pattern", ] before.collect.collectDirs = [ CollectDir("/etc", collectMode="daily"), ] self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_017a(self): """ Extract document containing only a valid collect section, non-empty lists, validate=True. (Test a file.) """ before = Config() before.collect = CollectConfig() before.collect.targetDir = "/opt/backup/collect" before.collect.archiveMode = "targz" before.collect.ignoreFile = ".cbignore" before.collect.absoluteExcludePaths = [ "/one", "/two", "/three", ] before.collect.excludePatterns = [ "pattern", ] before.collect.collectFiles = [ CollectFile("/etc", collectMode="daily"), ] self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_018(self): """ Extract document containing only a valid collect section, non-empty lists, validate=False. (Test a directory.) """ before = Config() before.collect = CollectConfig() before.collect.targetDir = "/opt/backup/collect" before.collect.archiveMode = "targz" before.collect.ignoreFile = ".cbignore" before.collect.absoluteExcludePaths = [ "/one", "/two", "/three", ] before.collect.excludePatterns = [ "pattern", ] before.collect.collectDirs = [ CollectDir("/etc", collectMode="daily"), ] beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_018a(self): """ Extract document containing only a valid collect section, non-empty lists, validate=False. (Test a file.) """ before = Config() before.collect = CollectConfig() before.collect.targetDir = "/opt/backup/collect" before.collect.archiveMode = "targz" before.collect.ignoreFile = ".cbignore" before.collect.absoluteExcludePaths = [ "/one", "/two", "/three", ] before.collect.excludePatterns = [ "pattern", ] before.collect.collectFiles = [ CollectFile("/etc", collectMode="daily"), ] beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_019(self): """ Extract document containing only an invalid collect section, validate=True. """ before = Config() before.collect = CollectConfig() self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_020(self): """ Extract document containing only an invalid collect section, validate=False. """ before = Config() before.collect = CollectConfig() beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_021(self): """ Extract document containing only a valid stage section, one empty list, validate=True. """ before = Config() before.stage = StageConfig() before.stage.targetDir = "/opt/backup/staging" before.stage.localPeers = [ LocalPeer("machine1", "/opt/backup/collect"), ] before.stage.remotePeers = None self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_022(self): """ Extract document containing only a valid stage section, empty lists, validate=False. """ before = Config() before.stage = StageConfig() before.stage.targetDir = "/opt/backup/staging" before.stage.localPeers = [ LocalPeer("machine1", "/opt/backup/collect"), ] before.stage.remotePeers = None beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_023(self): """ Extract document containing only a valid stage section, non-empty lists, validate=True. """ before = Config() before.stage = StageConfig() before.stage.targetDir = "/opt/backup/staging" before.stage.localPeers = [ LocalPeer("machine1", "/opt/backup/collect"), ] before.stage.remotePeers = [ RemotePeer("machine2", "/opt/backup/collect", remoteUser="backup"), ] self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_024(self): """ Extract document containing only a valid stage section, non-empty lists, validate=False. """ before = Config() before.stage = StageConfig() before.stage.targetDir = "/opt/backup/staging" before.stage.localPeers = [ LocalPeer("machine1", "/opt/backup/collect"), ] before.stage.remotePeers = [ RemotePeer("machine2", "/opt/backup/collect", remoteUser="backup"), ] beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_025(self): """ Extract document containing only an invalid stage section, validate=True. """ before = Config() before.stage = StageConfig() self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_026(self): """ Extract document containing only an invalid stage section, validate=False. """ before = Config() before.stage = StageConfig() beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_027(self): """ Extract document containing only a valid store section, validate=True. """ before = Config() before.store = StoreConfig() before.store.sourceDir = "/opt/backup/staging" before.store.devicePath = "/dev/cdrw" before.store.deviceScsiId = "0,0,0" before.store.driveSpeed = 4 before.store.mediaType = "cdrw-74" before.store.checkData = True before.store.checkMedia = True before.store.warnMidnite = True before.store.noEject = True before.store.refreshMediaDelay = 12 before.store.ejectDelay = 13 self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_028(self): """ Extract document containing only a valid store section, validate=False. """ before = Config() before.store = StoreConfig() before.store.sourceDir = "/opt/backup/staging" before.store.devicePath = "/dev/cdrw" before.store.deviceScsiId = "0,0,0" before.store.driveSpeed = 4 before.store.mediaType = "cdrw-74" before.store.checkData = True before.store.checkMedia = True before.store.warnMidnite = True before.store.noEject = True before.store.refreshMediaDelay = 12 before.store.ejectDelay = 13 beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_029(self): """ Extract document containing only an invalid store section, validate=True. """ before = Config() before.store = StoreConfig() self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_030(self): """ Extract document containing only an invalid store section, validate=False. """ before = Config() before.store = StoreConfig() beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_031(self): """ Extract document containing only a valid purge section, empty list, validate=True. """ before = Config() before.purge = PurgeConfig() self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_032(self): """ Extract document containing only a valid purge section, empty list, validate=False. """ before = Config() before.purge = PurgeConfig() beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_033(self): """ Extract document containing only a valid purge section, non-empty list, validate=True. """ before = Config() before.purge = PurgeConfig() before.purge.purgeDirs = [] before.purge.purgeDirs.append(PurgeDir(absolutePath="/whatever", retainDays=3)) self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_034(self): """ Extract document containing only a valid purge section, non-empty list, validate=False. """ before = Config() before.purge = PurgeConfig() before.purge.purgeDirs = [] before.purge.purgeDirs.append(PurgeDir(absolutePath="/whatever", retainDays=3)) beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_035(self): """ Extract document containing only an invalid purge section, validate=True. """ before = Config() before.purge = PurgeConfig() before.purge.purgeDirs = [] before.purge.purgeDirs.append(PurgeDir(absolutePath="/whatever")) self.assertRaises(ValueError, before.extractXml, validate=True) def testExtractXml_036(self): """ Extract document containing only an invalid purge section, validate=False. """ before = Config() before.purge = PurgeConfig() before.purge.purgeDirs = [] before.purge.purgeDirs.append(PurgeDir(absolutePath="/whatever")) beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_037(self): """ Extract complete document containing all required and optional fields, "index" extensions, validate=False. """ path = self.resources["cback.conf.15"] before = Config(xmlPath=path, validate=False) beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_037a(self): """ Extract complete document containing all required and optional fields, "dependency" extensions, validate=False. """ path = self.resources["cback.conf.20"] before = Config(xmlPath=path, validate=False) beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_038(self): """ Extract complete document containing all required and optional fields, "index" extensions, validate=True. """ path = self.resources["cback.conf.15"] before = Config(xmlPath=path, validate=True) beforeXml = before.extractXml(validate=True) after = Config(xmlData=beforeXml, validate=True) self.assertEqual(before, after) def testExtractXml_038a(self): """ Extract complete document containing all required and optional fields, "dependency" extensions, validate=True. """ path = self.resources["cback.conf.20"] before = Config(xmlPath=path, validate=True) beforeXml = before.extractXml(validate=True) after = Config(xmlData=beforeXml, validate=True) self.assertEqual(before, after) def testExtractXml_039(self): """ Extract a sample from Cedar Backup v1.x, which must still be valid, validate=False. """ path = self.resources["cback.conf.1"] before = Config(xmlPath=path, validate=False) beforeXml = before.extractXml(validate=False) after = Config(xmlData=beforeXml, validate=False) self.assertEqual(before, after) def testExtractXml_040(self): """ Extract a sample from Cedar Backup v1.x, which must still be valid, validate=True. """ path = self.resources["cback.conf.1"] before = Config(xmlPath=path, validate=True) beforeXml = before.extractXml(validate=True) after = Config(xmlData=beforeXml, validate=True) self.assertEqual(before, after) def testExtractXml_041(self): """ Extract complete document containing all required and optional fields, using a peers configuration section, validate=True. """ path = self.resources["cback.conf.21"] before = Config(xmlPath=path, validate=True) beforeXml = before.extractXml(validate=True) after = Config(xmlData=beforeXml, validate=True) self.assertEqual(before, after) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1569562 cedar_backup3-3.8.1/tests/test_customize.py0000644000000000000000000001767314567004737015767 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests customization functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/customize.py. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.config import CommandOverride, Config, OptionsConfig from CedarBackup3.customize import PLATFORM, customizeOverrides from CedarBackup3.testutil import configureLogging ####################################################################### # Test Case Classes ####################################################################### ###################### # TestFunctions class ###################### class TestFunctions(unittest.TestCase): """Tests for the various public functions.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ############################ # Test customizeOverrides() ############################ def testCustomizeOverrides_001(self): """ Test platform=standard, no existing overrides. """ config = Config() options = OptionsConfig() if PLATFORM == "standard": config.options = options customizeOverrides(config) self.assertEqual(None, options.overrides) config.options = options customizeOverrides(config, platform="standard") self.assertEqual(None, options.overrides) def testCustomizeOverrides_002(self): """ Test platform=standard, existing override for cdrecord. """ config = Config() options = OptionsConfig() options.overrides = [ CommandOverride("cdrecord", "/blech"), ] if PLATFORM == "standard": config.options = options customizeOverrides(config) self.assertEqual([CommandOverride("cdrecord", "/blech")], options.overrides) config.options = options customizeOverrides(config, platform="standard") self.assertEqual([CommandOverride("cdrecord", "/blech")], options.overrides) def testCustomizeOverrides_003(self): """ Test platform=standard, existing override for mkisofs. """ config = Config() options = OptionsConfig() options.overrides = [ CommandOverride("mkisofs", "/blech"), ] if PLATFORM == "standard": config.options = options customizeOverrides(config) self.assertEqual([CommandOverride("mkisofs", "/blech")], options.overrides) config.options = options customizeOverrides(config, platform="standard") self.assertEqual([CommandOverride("mkisofs", "/blech")], options.overrides) def testCustomizeOverrides_004(self): """ Test platform=standard, existing override for cdrecord and mkisofs. """ config = Config() options = OptionsConfig() options.overrides = [ CommandOverride("cdrecord", "/blech"), CommandOverride("mkisofs", "/blech2"), ] if PLATFORM == "standard": config.options = options customizeOverrides(config) self.assertEqual([CommandOverride("cdrecord", "/blech"), CommandOverride("mkisofs", "/blech2")], options.overrides) config.options = options customizeOverrides(config, platform="standard") self.assertEqual([CommandOverride("cdrecord", "/blech"), CommandOverride("mkisofs", "/blech2")], options.overrides) def testCustomizeOverrides_005(self): """ Test platform=debian, no existing overrides. """ config = Config() options = OptionsConfig() if PLATFORM == "debian": config.options = options customizeOverrides(config) self.assertEqual( [CommandOverride("cdrecord", "/usr/bin/wodim"), CommandOverride("mkisofs", "/usr/bin/genisoimage")], options.overrides, ) config.options = options customizeOverrides(config, platform="debian") self.assertEqual( [CommandOverride("cdrecord", "/usr/bin/wodim"), CommandOverride("mkisofs", "/usr/bin/genisoimage")], options.overrides ) def testCustomizeOverrides_006(self): """ Test platform=debian, existing override for cdrecord. """ config = Config() options = OptionsConfig() options.overrides = [ CommandOverride("cdrecord", "/blech"), ] if PLATFORM == "debian": config.options = options customizeOverrides(config) self.assertEqual( [CommandOverride("cdrecord", "/blech"), CommandOverride("mkisofs", "/usr/bin/genisoimage")], options.overrides ) config.options = options customizeOverrides(config, platform="debian") self.assertEqual( [CommandOverride("cdrecord", "/blech"), CommandOverride("mkisofs", "/usr/bin/genisoimage")], options.overrides ) def testCustomizeOverrides_007(self): """ Test platform=debian, existing override for mkisofs. """ config = Config() options = OptionsConfig() options.overrides = [ CommandOverride("mkisofs", "/blech"), ] if PLATFORM == "debian": config.options = options customizeOverrides(config) self.assertEqual( [CommandOverride("cdrecord", "/usr/bin/wodim"), CommandOverride("mkisofs", "/blech")], options.overrides ) config.options = options customizeOverrides(config, platform="debian") self.assertEqual([CommandOverride("cdrecord", "/usr/bin/wodim"), CommandOverride("mkisofs", "/blech")], options.overrides) def testCustomizeOverrides_008(self): """ Test platform=debian, existing override for cdrecord and mkisofs. """ config = Config() options = OptionsConfig() options.overrides = [ CommandOverride("cdrecord", "/blech"), CommandOverride("mkisofs", "/blech2"), ] if PLATFORM == "debian": config.options = options customizeOverrides(config) self.assertEqual([CommandOverride("cdrecord", "/blech"), CommandOverride("mkisofs", "/blech2")], options.overrides) config.options = options customizeOverrides(config, platform="debian") self.assertEqual([CommandOverride("cdrecord", "/blech"), CommandOverride("mkisofs", "/blech2")], options.overrides) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1569562 cedar_backup3-3.8.1/tests/test_dvdwriter.py0000644000000000000000000014444514567004737015755 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests DVD writer functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/writers/dvdwriter.py. Code Coverage ============= This module contains individual tests for the public classes implemented in dvdwriter.py. Unfortunately, it's rather difficult to test this code in an automated fashion, even if you have access to a physical DVD writer drive. It's even more difficult to test it if you are running on some build daemon (think of a Debian autobuilder) which can't be expected to have any hardware or any media that you could write to. Because of this, there aren't any tests below that actually cause DVD media to be written to. As a compromise, complicated parts of the implementation are in terms of private static methods with well-defined behaviors. Normally, I prefer to only test the public interface to class, but in this case, testing these few private methods will help give us some reasonable confidence in the code, even if we can't write a physical disc or can't run all of the tests. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== Some Cedar Backup regression tests require a specialized environment in order to run successfully. This environment won't necessarily be available on every build system out there (for instance, on a Debian autobuilder). Because of this, the default behavior is to run a "reduced feature set" test suite that has no surprising system, kernel or network requirements. There are no special dependencies for these tests. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import os import tempfile import unittest from CedarBackup3.testutil import buildPath, configureLogging, extractTar, findResources, removedir from CedarBackup3.writers.dvdwriter import MEDIA_DVDPLUSR, MEDIA_DVDPLUSRW, DvdWriter, MediaCapacity, MediaDefinition ####################################################################### # Module-wide configuration and constants ####################################################################### GB44 = 4.4 * 1024.0 * 1024.0 * 1024.0 # 4.4 GB GB44SECTORS = GB44 / 2048.0 # 4.4 GB in 2048-byte sectors DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "tree9.tar.gz", ] ####################################################################### # Test Case Classes ####################################################################### ############################ # TestMediaDefinition class ############################ class TestMediaDefinition(unittest.TestCase): """Tests for the MediaDefinition class.""" @classmethod def setUpClass(cls): configureLogging() def testConstructor_001(self): """ Test the constructor with an invalid media type. """ self.assertRaises(ValueError, MediaDefinition, 100) def testConstructor_002(self): """ Test the constructor with the ``MEDIA_DVDPLUSR`` media type. """ media = MediaDefinition(MEDIA_DVDPLUSR) self.assertEqual(MEDIA_DVDPLUSR, media.mediaType) self.assertEqual(False, media.rewritable) self.assertEqual(GB44SECTORS, media.capacity) def testConstructor_003(self): """ Test the constructor with the ``MEDIA_DVDPLUSRW`` media type. """ media = MediaDefinition(MEDIA_DVDPLUSRW) self.assertEqual(MEDIA_DVDPLUSRW, media.mediaType) self.assertEqual(True, media.rewritable) self.assertEqual(GB44SECTORS, media.capacity) ########################## # TestMediaCapacity class ########################## class TestMediaCapacity(unittest.TestCase): """Tests for the MediaCapacity class.""" @classmethod def setUpClass(cls): configureLogging() def testConstructor_001(self): """ Test the constructor with valid, zero values """ capacity = MediaCapacity(0.0, 0.0) self.assertEqual(0.0, capacity.bytesUsed) self.assertEqual(0.0, capacity.bytesAvailable) def testConstructor_002(self): """ Test the constructor with valid, non-zero values. """ capacity = MediaCapacity(1.1, 2.2) self.assertEqual(1.1, capacity.bytesUsed) self.assertEqual(2.2, capacity.bytesAvailable) def testConstructor_003(self): """ Test the constructor with bytesUsed that is not a float. """ self.assertRaises(ValueError, MediaCapacity, 0.0, "ken") def testConstructor_004(self): """ Test the constructor with bytesAvailable that is not a float. """ self.assertRaises(ValueError, MediaCapacity, "a", 0.0) ###################### # TestDvdWriter class ###################### class TestDvdWriter(unittest.TestCase): """Tests for the DvdWriter class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): removedir(self.tmpdir) ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) def getFileContents(self, resource): """Gets contents of named resource as a list of strings.""" path = self.resources[resource] with open(path) as f: return f.readlines() ################### # Test constructor ################### def testConstructor_001(self): """ Test with an empty device. """ self.assertRaises(ValueError, DvdWriter, None) def testConstructor_002(self): """ Test with a device only. """ dvdwriter = DvdWriter("/dev/dvd", unittest=True) self.assertEqual("/dev/dvd", dvdwriter.device) self.assertEqual(None, dvdwriter.scsiId) self.assertEqual("/dev/dvd", dvdwriter.hardwareId) self.assertEqual(None, dvdwriter.driveSpeed) self.assertEqual(MEDIA_DVDPLUSRW, dvdwriter.media.mediaType) self.assertEqual(True, dvdwriter.deviceHasTray) self.assertEqual(True, dvdwriter.deviceCanEject) def testConstructor_003(self): """ Test with a device and valid SCSI id. """ dvdwriter = DvdWriter("/dev/dvd", scsiId="ATA:1,0,0", unittest=True) self.assertEqual("/dev/dvd", dvdwriter.device) self.assertEqual("ATA:1,0,0", dvdwriter.scsiId) self.assertEqual("/dev/dvd", dvdwriter.hardwareId) self.assertEqual(None, dvdwriter.driveSpeed) self.assertEqual(MEDIA_DVDPLUSRW, dvdwriter.media.mediaType) self.assertEqual(True, dvdwriter.deviceHasTray) self.assertEqual(True, dvdwriter.deviceCanEject) def testConstructor_004(self): """ Test with a device and valid drive speed. """ dvdwriter = DvdWriter("/dev/dvd", driveSpeed=3, unittest=True) self.assertEqual("/dev/dvd", dvdwriter.device) self.assertEqual(None, dvdwriter.scsiId) self.assertEqual("/dev/dvd", dvdwriter.hardwareId) self.assertEqual(3, dvdwriter.driveSpeed) self.assertEqual(MEDIA_DVDPLUSRW, dvdwriter.media.mediaType) self.assertEqual(True, dvdwriter.deviceHasTray) self.assertEqual(True, dvdwriter.deviceCanEject) def testConstructor_005(self): """ Test with a device with media type MEDIA_DVDPLUSR. """ dvdwriter = DvdWriter("/dev/dvd", mediaType=MEDIA_DVDPLUSR, unittest=True) self.assertEqual("/dev/dvd", dvdwriter.device) self.assertEqual(None, dvdwriter.scsiId) self.assertEqual("/dev/dvd", dvdwriter.hardwareId) self.assertEqual(None, dvdwriter.driveSpeed) self.assertEqual(MEDIA_DVDPLUSR, dvdwriter.media.mediaType) self.assertEqual(True, dvdwriter.deviceHasTray) self.assertEqual(True, dvdwriter.deviceCanEject) def testConstructor_006(self): """ Test with a device with media type MEDIA_DVDPLUSRW. """ dvdwriter = DvdWriter("/dev/dvd", mediaType=MEDIA_DVDPLUSR, unittest=True) self.assertEqual("/dev/dvd", dvdwriter.device) self.assertEqual(None, dvdwriter.scsiId) self.assertEqual("/dev/dvd", dvdwriter.hardwareId) self.assertEqual(None, dvdwriter.driveSpeed) self.assertEqual(MEDIA_DVDPLUSR, dvdwriter.media.mediaType) self.assertEqual(True, dvdwriter.deviceHasTray) self.assertEqual(True, dvdwriter.deviceCanEject) def testConstructor_007(self): """ Test with a device and invalid SCSI id. """ dvdwriter = DvdWriter("/dev/dvd", scsiId="00000000", unittest=True) self.assertEqual("/dev/dvd", dvdwriter.device) self.assertEqual("00000000", dvdwriter.scsiId) self.assertEqual("/dev/dvd", dvdwriter.hardwareId) self.assertEqual(None, dvdwriter.driveSpeed) self.assertEqual(MEDIA_DVDPLUSRW, dvdwriter.media.mediaType) self.assertEqual(True, dvdwriter.deviceHasTray) self.assertEqual(True, dvdwriter.deviceCanEject) def testConstructor_008(self): """ Test with a device and invalid drive speed. """ self.assertRaises(ValueError, DvdWriter, "/dev/dvd", driveSpeed="KEN", unittest=True) def testConstructor_009(self): """ Test with a device and invalid media type. """ self.assertRaises(ValueError, DvdWriter, "/dev/dvd", mediaType=999, unittest=True) def testConstructor_010(self): """ Test with all valid parameters, but no device, unittest=True. """ self.assertRaises(ValueError, DvdWriter, None, "ATA:1,0,0", 1, MEDIA_DVDPLUSRW, unittest=True) def testConstructor_011(self): """ Test with all valid parameters, but no device, unittest=False. """ self.assertRaises(ValueError, DvdWriter, None, "ATA:1,0,0", 1, MEDIA_DVDPLUSRW, unittest=False) def testConstructor_012(self): """ Test with all valid parameters, and an invalid device (not absolute path), unittest=True. """ self.assertRaises(ValueError, DvdWriter, "dev/dvd", "ATA:1,0,0", 1, MEDIA_DVDPLUSRW, unittest=True) def testConstructor_013(self): """ Test with all valid parameters, and an invalid device (not absolute path), unittest=False. """ self.assertRaises(ValueError, DvdWriter, "dev/dvd", "ATA:1,0,0", 1, MEDIA_DVDPLUSRW, unittest=False) def testConstructor_014(self): """ Test with all valid parameters, and an invalid device (path does not exist), unittest=False. """ self.assertRaises(ValueError, DvdWriter, "/dev/bogus", "ATA:1,0,0", 1, MEDIA_DVDPLUSRW, unittest=False) def testConstructor_015(self): """ Test with all valid parameters. """ dvdwriter = DvdWriter("/dev/dvd", "ATA:1,0,0", 1, MEDIA_DVDPLUSR, noEject=False, unittest=True) self.assertEqual("/dev/dvd", dvdwriter.device) self.assertEqual("ATA:1,0,0", dvdwriter.scsiId) self.assertEqual("/dev/dvd", dvdwriter.hardwareId) self.assertEqual(1, dvdwriter.driveSpeed) self.assertEqual(MEDIA_DVDPLUSR, dvdwriter.media.mediaType) self.assertEqual(True, dvdwriter.deviceHasTray) self.assertEqual(True, dvdwriter.deviceCanEject) def testConstructor_016(self): """ Test with all valid parameters. """ dvdwriter = DvdWriter("/dev/dvd", "ATA:1,0,0", 1, MEDIA_DVDPLUSR, noEject=True, unittest=True) self.assertEqual("/dev/dvd", dvdwriter.device) self.assertEqual("ATA:1,0,0", dvdwriter.scsiId) self.assertEqual("/dev/dvd", dvdwriter.hardwareId) self.assertEqual(1, dvdwriter.driveSpeed) self.assertEqual(MEDIA_DVDPLUSR, dvdwriter.media.mediaType) self.assertEqual(False, dvdwriter.deviceHasTray) self.assertEqual(False, dvdwriter.deviceCanEject) ###################### # Test isRewritable() ###################### def testIsRewritable_001(self): """ Test with MEDIA_DVDPLUSR. """ dvdwriter = DvdWriter("/dev/dvd", mediaType=MEDIA_DVDPLUSR, unittest=True) self.assertEqual(False, dvdwriter.isRewritable()) def testIsRewritable_002(self): """ Test with MEDIA_DVDPLUSRW. """ dvdwriter = DvdWriter("/dev/dvd", mediaType=MEDIA_DVDPLUSRW, unittest=True) self.assertEqual(True, dvdwriter.isRewritable()) ######################### # Test initializeImage() ######################### def testInitializeImage_001(self): """ Test with newDisc=False, tmpdir=None. """ dvdwriter = DvdWriter("/dev/dvd", unittest=True) dvdwriter.initializeImage(False, None) self.assertEqual(False, dvdwriter._image.newDisc) self.assertEqual(None, dvdwriter._image.tmpdir) self.assertEqual({}, dvdwriter._image.entries) def testInitializeImage_002(self): """ Test with newDisc=True, tmpdir not None. """ dvdwriter = DvdWriter("/dev/dvd", unittest=True) dvdwriter.initializeImage(True, "/path/to/somewhere") self.assertEqual(True, dvdwriter._image.newDisc) self.assertEqual("/path/to/somewhere", dvdwriter._image.tmpdir) self.assertEqual({}, dvdwriter._image.entries) ####################### # Test addImageEntry() ####################### def testAddImageEntry_001(self): """ Add a valid path with no graft point, before calling initializeImage(). """ self.extractTar("tree9") path = self.buildPath(["tree9", "dir002"]) self.assertTrue(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) self.assertRaises(ValueError, dvdwriter.addImageEntry, path, None) def testAddImageEntry_002(self): """ Add a valid path with a graft point, before calling initializeImage(). """ self.extractTar("tree9") path = self.buildPath(["tree9", "dir002"]) self.assertTrue(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) self.assertRaises(ValueError, dvdwriter.addImageEntry, path, "ken") def testAddImageEntry_003(self): """ Add a non-existent path with no graft point, before calling initializeImage(). """ self.extractTar("tree9") path = self.buildPath(["tree9", "bogus"]) self.assertFalse(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) self.assertRaises(ValueError, dvdwriter.addImageEntry, path, None) def testAddImageEntry_004(self): """ Add a non-existent path with a graft point, before calling initializeImage(). """ self.extractTar("tree9") path = self.buildPath(["tree9", "bogus"]) self.assertFalse(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) self.assertRaises(ValueError, dvdwriter.addImageEntry, path, "ken") def testAddImageEntry_005(self): """ Add a valid path with no graft point, after calling initializeImage(). """ self.extractTar("tree9") path = self.buildPath(["tree9", "dir002"]) self.assertTrue(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) dvdwriter.initializeImage(False, None) dvdwriter.addImageEntry(path, None) self.assertEqual({path: None}, dvdwriter._image.entries) def testAddImageEntry_006(self): """ Add a valid path with a graft point, after calling initializeImage(). """ self.extractTar("tree9") path = self.buildPath(["tree9", "dir002"]) self.assertTrue(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) dvdwriter.initializeImage(False, None) dvdwriter.addImageEntry(path, "ken") self.assertEqual({path: "ken"}, dvdwriter._image.entries) def testAddImageEntry_007(self): """ Add a non-existent path with no graft point, after calling initializeImage(). """ self.extractTar("tree9") path = self.buildPath(["tree9", "bogus"]) self.assertFalse(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) dvdwriter.initializeImage(False, None) self.assertRaises(ValueError, dvdwriter.addImageEntry, path, None) def testAddImageEntry_008(self): """ Add a non-existent path with a graft point, after calling initializeImage(). """ self.extractTar("tree9") path = self.buildPath(["tree9", "bogus"]) self.assertFalse(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) dvdwriter.initializeImage(False, None) self.assertRaises(ValueError, dvdwriter.addImageEntry, path, "ken") def testAddImageEntry_009(self): """ Add the same path several times. """ self.extractTar("tree9") path = self.buildPath(["tree9", "dir002"]) self.assertTrue(os.path.exists(path)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) dvdwriter.initializeImage(False, None) dvdwriter.addImageEntry(path, "ken") self.assertEqual({path: "ken"}, dvdwriter._image.entries) dvdwriter.addImageEntry(path, "ken") self.assertEqual({path: "ken"}, dvdwriter._image.entries) dvdwriter.addImageEntry(path, "ken") self.assertEqual({path: "ken"}, dvdwriter._image.entries) dvdwriter.addImageEntry(path, "ken") self.assertEqual({path: "ken"}, dvdwriter._image.entries) def testAddImageEntry_010(self): """ Add several paths. """ self.extractTar("tree9") path1 = self.buildPath(["tree9", "dir001"]) path2 = self.buildPath(["tree9", "dir002"]) path3 = self.buildPath(["tree9", "dir001", "dir001"]) self.assertTrue(os.path.exists(path1)) self.assertTrue(os.path.exists(path2)) self.assertTrue(os.path.exists(path3)) dvdwriter = DvdWriter("/dev/dvd", unittest=True) dvdwriter.initializeImage(False, None) dvdwriter.addImageEntry(path1, None) self.assertEqual({path1: None}, dvdwriter._image.entries) dvdwriter.addImageEntry(path2, "ken") self.assertEqual({path1: None, path2: "ken"}, dvdwriter._image.entries) dvdwriter.addImageEntry(path3, "another") self.assertEqual({path1: None, path2: "ken", path3: "another"}, dvdwriter._image.entries) ############################ # Test _searchForOverburn() ############################ def testSearchForOverburn_001(self): """ Test with output=None. """ output = None DvdWriter._searchForOverburn(output) # no exception should be thrown def testSearchForOverburn_002(self): """ Test with output=[]. """ output = [] DvdWriter._searchForOverburn(output) # no exception should be thrown def testSearchForOverburn_003(self): """ Test with one-line output, not containing the pattern. """ output = [ "This line does not contain the pattern", ] DvdWriter._searchForOverburn(output) # no exception should be thrown output = [ ":-( /dev/cdrom: blocks are free, to be written!", ] DvdWriter._searchForOverburn(output) # no exception should be thrown output = [ ":-) /dev/cdrom: 89048 blocks are free, 2033746 to be written!", ] DvdWriter._searchForOverburn(output) # no exception should be thrown output = [ ":-( /dev/cdrom: 894048blocks are free, 2033746to be written!", ] DvdWriter._searchForOverburn(output) # no exception should be thrown def testSearchForOverburn_004(self): """ Test with one-line output(s), containing the pattern. """ output = [ ":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!", ] self.assertRaises(IOError, DvdWriter._searchForOverburn, output) output = [ ":-( /dev/cdrom: XXXX blocks are free, XXXX to be written!", ] self.assertRaises(IOError, DvdWriter._searchForOverburn, output) output = [ ":-( /dev/cdrom: 1 blocks are free, 1 to be written!", ] self.assertRaises(IOError, DvdWriter._searchForOverburn, output) output = [ ":-( /dev/cdrom: 0 blocks are free, 0 to be written!", ] self.assertRaises(IOError, DvdWriter._searchForOverburn, output) output = [ ":-( /dev/dvd: 0 blocks are free, 0 to be written!", ] self.assertRaises(IOError, DvdWriter._searchForOverburn, output) output = [ ":-( /dev/writer: 0 blocks are free, 0 to be written!", ] self.assertRaises(IOError, DvdWriter._searchForOverburn, output) output = [ ":-( bogus: 0 blocks are free, 0 to be written!", ] self.assertRaises(IOError, DvdWriter._searchForOverburn, output) def testSearchForOverburn_005(self): """ Test with multi-line output, not containing the pattern. """ output = [] output.append( "Executing 'mkisofs -C 973744,1401056 -M /dev/fd/3 -r -graft-points music4/=music | builtin_dd of=/dev/cdrom obs=32k seek=87566'" ) output.append("Rock Ridge signatures found") output.append("Using THE_K000 for music4/The_Kings_Singers (The_Kingston_Trio)") output.append( "Using COCKT000 for music/Various_Artists/Cocktail_Classics_-_Beethovens_Fifth_and_Others (Cocktail_Classics_-_Pachelbels_Canon_and_Others)" ) output.append( "Using THE_V000 for music/Brahms/The_Violin_Sonatas (The_Viola_Sonatas) Using COMPL000 for music/Gershwin/Complete_Gershwin_2 (Complete_Gershwin_1)" ) output.append( "Using SELEC000.MP3;1 for music/Marquette_Chorus/Selected_Christmas_Carols_For_Double_Choir.mp3 (Selected_Choruses_from_The_Lark.mp3)" ) output.append( "Using SELEC001.MP3;1 for music/Marquette_Chorus/Selected_Choruses_from_The_Lark.mp3 (Selected_Choruses_from_Messiah.mp3)" ) output.append( "Using IN_TH000.MP3;1 for music/Marquette_Chorus/In_the_Bleak_Midwinter.mp3 (In_the_Beginning.mp3) Using AFRIC000.MP3;1 for music/Marquette_Chorus/African_Noel-tb.mp3 (African_Noel-satb.mp3)" ) DvdWriter._searchForOverburn(output) # no exception should be thrown") def testSearchForOverburn_006(self): """ Test with multi-line output, containing the pattern at the top. """ output = [] output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append( "Executing 'mkisofs -C 973744,1401056 -M /dev/fd/3 -r -graft-points music4/=music | builtin_dd of=/dev/cdrom obs=32k seek=87566'" ) output.append("Rock Ridge signatures found") output.append("Using THE_K000 for music4/The_Kings_Singers (The_Kingston_Trio)") output.append( "Using COCKT000 for music/Various_Artists/Cocktail_Classics_-_Beethovens_Fifth_and_Others (Cocktail_Classics_-_Pachelbels_Canon_and_Others)" ) output.append( "Using THE_V000 for music/Brahms/The_Violin_Sonatas (The_Viola_Sonatas) Using COMPL000 for music/Gershwin/Complete_Gershwin_2 (Complete_Gershwin_1)" ) output.append( "Using SELEC000.MP3;1 for music/Marquette_Chorus/Selected_Christmas_Carols_For_Double_Choir.mp3 (Selected_Choruses_from_The_Lark.mp3)" ) output.append( "Using SELEC001.MP3;1 for music/Marquette_Chorus/Selected_Choruses_from_The_Lark.mp3 (Selected_Choruses_from_Messiah.mp3)" ) output.append( "Using IN_TH000.MP3;1 for music/Marquette_Chorus/In_the_Bleak_Midwinter.mp3 (In_the_Beginning.mp3) Using AFRIC000.MP3;1 for music/Marquette_Chorus/African_Noel-tb.mp3 (African_Noel-satb.mp3)" ) self.assertRaises(IOError, DvdWriter._searchForOverburn, output) def testSearchForOverburn_007(self): """ Test with multi-line output, containing the pattern at the bottom. """ output = [] output.append( "Executing 'mkisofs -C 973744,1401056 -M /dev/fd/3 -r -graft-points music4/=music | builtin_dd of=/dev/cdrom obs=32k seek=87566'" ) output.append("Rock Ridge signatures found") output.append("Using THE_K000 for music4/The_Kings_Singers (The_Kingston_Trio)") output.append( "Using COCKT000 for music/Various_Artists/Cocktail_Classics_-_Beethovens_Fifth_and_Others (Cocktail_Classics_-_Pachelbels_Canon_and_Others)" ) output.append( "Using THE_V000 for music/Brahms/The_Violin_Sonatas (The_Viola_Sonatas) Using COMPL000 for music/Gershwin/Complete_Gershwin_2 (Complete_Gershwin_1)" ) output.append( "Using SELEC000.MP3;1 for music/Marquette_Chorus/Selected_Christmas_Carols_For_Double_Choir.mp3 (Selected_Choruses_from_The_Lark.mp3)" ) output.append( "Using SELEC001.MP3;1 for music/Marquette_Chorus/Selected_Choruses_from_The_Lark.mp3 (Selected_Choruses_from_Messiah.mp3)" ) output.append( "Using IN_TH000.MP3;1 for music/Marquette_Chorus/In_the_Bleak_Midwinter.mp3 (In_the_Beginning.mp3) Using AFRIC000.MP3;1 for music/Marquette_Chorus/African_Noel-tb.mp3 (African_Noel-satb.mp3)" ) output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") self.assertRaises(IOError, DvdWriter._searchForOverburn, output) def testSearchForOverburn_008(self): """ Test with multi-line output, containing the pattern in the middle. """ output = [] output.append( "Executing 'mkisofs -C 973744,1401056 -M /dev/fd/3 -r -graft-points music4/=music | builtin_dd of=/dev/cdrom obs=32k seek=87566'" ) output.append("Rock Ridge signatures found") output.append("Using THE_K000 for music4/The_Kings_Singers (The_Kingston_Trio)") output.append( "Using COCKT000 for music/Various_Artists/Cocktail_Classics_-_Beethovens_Fifth_and_Others (Cocktail_Classics_-_Pachelbels_Canon_and_Others)" ) output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append( "Using THE_V000 for music/Brahms/The_Violin_Sonatas (The_Viola_Sonatas) Using COMPL000 for music/Gershwin/Complete_Gershwin_2 (Complete_Gershwin_1)" ) output.append( "Using SELEC000.MP3;1 for music/Marquette_Chorus/Selected_Christmas_Carols_For_Double_Choir.mp3 (Selected_Choruses_from_The_Lark.mp3)" ) output.append( "Using SELEC001.MP3;1 for music/Marquette_Chorus/Selected_Choruses_from_The_Lark.mp3 (Selected_Choruses_from_Messiah.mp3)" ) output.append( "Using IN_TH000.MP3;1 for music/Marquette_Chorus/In_the_Bleak_Midwinter.mp3 (In_the_Beginning.mp3) Using AFRIC000.MP3;1 for music/Marquette_Chorus/African_Noel-tb.mp3 (African_Noel-satb.mp3)" ) self.assertRaises(IOError, DvdWriter._searchForOverburn, output) def testSearchForOverburn_009(self): """ Test with multi-line output, containing the pattern several times. """ output = [] output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append( "Executing 'mkisofs -C 973744,1401056 -M /dev/fd/3 -r -graft-points music4/=music | builtin_dd of=/dev/cdrom obs=32k seek=87566'" ) output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append("Rock Ridge signatures found") output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append("Using THE_K000 for music4/The_Kings_Singers (The_Kingston_Trio)") output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append( "Using COCKT000 for music/Various_Artists/Cocktail_Classics_-_Beethovens_Fifth_and_Others (Cocktail_Classics_-_Pachelbels_Canon_and_Others)" ) output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append( "Using THE_V000 for music/Brahms/The_Violin_Sonatas (The_Viola_Sonatas) Using COMPL000 for music/Gershwin/Complete_Gershwin_2 (Complete_Gershwin_1)" ) output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append( "Using SELEC000.MP3;1 for music/Marquette_Chorus/Selected_Christmas_Carols_For_Double_Choir.mp3 (Selected_Choruses_from_The_Lark.mp3)" ) output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append( "Using SELEC001.MP3;1 for music/Marquette_Chorus/Selected_Choruses_from_The_Lark.mp3 (Selected_Choruses_from_Messiah.mp3)" ) output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append( "Using IN_TH000.MP3;1 for music/Marquette_Chorus/In_the_Bleak_Midwinter.mp3 (In_the_Beginning.mp3) Using AFRIC000.MP3;1 for music/Marquette_Chorus/African_Noel-tb.mp3 (African_Noel-satb.mp3)" ) output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") output.append(":-( /dev/cdrom: 894048 blocks are free, 2033746 to be written!") self.assertRaises(IOError, DvdWriter._searchForOverburn, output) ########################### # Test _parseSectorsUsed() ########################### def testParseSectorsUsed_001(self): """ Test with output=None. """ output = None sectorsUsed = DvdWriter._parseSectorsUsed(output) self.assertEqual(0.0, sectorsUsed) def testParseSectorsUsed_002(self): """ Test with output=[]. """ output = [] sectorsUsed = DvdWriter._parseSectorsUsed(output) self.assertEqual(0.0, sectorsUsed) def testParseSectorsUsed_003(self): """ Test with one-line output, not containing the pattern. """ output = [ "This line does not contain the pattern", ] sectorsUsed = DvdWriter._parseSectorsUsed(output) self.assertEqual(0.0, sectorsUsed) def testParseSectorsUsed_004(self): """ Test with one-line output(s), containing the pattern. """ output = [ "'seek=10'", ] sectorsUsed = DvdWriter._parseSectorsUsed(output) self.assertEqual(10.0 * 16.0, sectorsUsed) output = [ "' seek= 10 '", ] sectorsUsed = DvdWriter._parseSectorsUsed(output) self.assertEqual(10.0 * 16.0, sectorsUsed) output = [ "Executing 'mkisofs -C 973744,1401056 -M /dev/fd/3 -r -graft-points music4/=music | builtin_dd of=/dev/cdrom obs=32k seek=87566'", ] sectorsUsed = DvdWriter._parseSectorsUsed(output) self.assertEqual(87566 * 16.0, sectorsUsed) def testParseSectorsUsed_005(self): """ Test with real growisofs output. """ output = [] output.append( "Executing 'mkisofs -C 973744,1401056 -M /dev/fd/3 -r -graft-points music4/=music | builtin_dd of=/dev/cdrom obs=32k seek=87566'" ) output.append("Rock Ridge signatures found") output.append("Using THE_K000 for music4/The_Kings_Singers (The_Kingston_Trio)") output.append( "Using COCKT000 for music/Various_Artists/Cocktail_Classics_-_Beethovens_Fifth_and_Others (Cocktail_Classics_-_Pachelbels_Canon_and_Others)" ) output.append( "Using THE_V000 for music/Brahms/The_Violin_Sonatas (The_Viola_Sonatas) Using COMPL000 for music/Gershwin/Complete_Gershwin_2 (Complete_Gershwin_1)" ) output.append( "Using SELEC000.MP3;1 for music/Marquette_Chorus/Selected_Christmas_Carols_For_Double_Choir.mp3 (Selected_Choruses_from_The_Lark.mp3)" ) output.append( "Using SELEC001.MP3;1 for music/Marquette_Chorus/Selected_Choruses_from_The_Lark.mp3 (Selected_Choruses_from_Messiah.mp3)" ) output.append( "Using IN_TH000.MP3;1 for music/Marquette_Chorus/In_the_Bleak_Midwinter.mp3 (In_the_Beginning.mp3) Using AFRIC000.MP3;1 for music/Marquette_Chorus/African_Noel-tb.mp3 (African_Noel-satb.mp3)" ) sectorsUsed = DvdWriter._parseSectorsUsed(output) self.assertEqual(87566 * 16.0, sectorsUsed) ######################### # Test _buildWriteArgs() ######################### def testBuildWriteArgs_001(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=None, imagePath=None, entries=None, mediaLabel=None,dryRun=False. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = None imagePath = None entries = None mediaLabel = None dryRun = False self.assertRaises( ValueError, DvdWriter._buildWriteArgs, newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun ) def testBuildWriteArgs_002(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=None, imagePath=None, entries=None, mediaLabel=None, dryRun=True. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = None imagePath = None entries = None mediaLabel = None dryRun = True self.assertRaises( ValueError, DvdWriter._buildWriteArgs, newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun ) def testBuildWriteArgs_003(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=None, imagePath="/path/to/image", entries=None, mediaLabel=None, dryRun=False. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = None imagePath = "/path/to/image" entries = None mediaLabel = None dryRun = False expected = [ "-use-the-force-luke=tty", "-M", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_004(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=None, imagePath="/path/to/image", entries=None, mediaLabel=None, dryRun=True. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = None imagePath = "/path/to/image" entries = None mediaLabel = None dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-M", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_005(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=None, imagePath="/path/to/image", entries=None, mediaLabel=None, dryRun=False. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = None imagePath = "/path/to/image" entries = None mediaLabel = None dryRun = False expected = [ "-use-the-force-luke=tty", "-Z", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_006(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=None, imagePath="/path/to/image", entries=None, mediaLabel=None, dryRun=True. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = None imagePath = "/path/to/image" entries = None mediaLabel = None dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-Z", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_007(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=1, imagePath="/path/to/image", entries=None, mediaLabel=None, dryRun=False. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = 1 imagePath = "/path/to/image" entries = None mediaLabel = None dryRun = False expected = [ "-use-the-force-luke=tty", "-speed=1", "-M", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_008(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=2, imagePath="/path/to/image", entries=None, mediaLabel=None, dryRun=True. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = 2 imagePath = "/path/to/image" entries = None mediaLabel = None dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-speed=2", "-M", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_009(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=3, imagePath="/path/to/image", entries=None, mediaLabel=None, dryRun=False. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = 3 imagePath = "/path/to/image" entries = None mediaLabel = None dryRun = False expected = [ "-use-the-force-luke=tty", "-speed=3", "-Z", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_010(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=4, imagePath="/path/to/image", entries=None, mediaLabel=None, dryRun=True. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = 4 imagePath = "/path/to/image" entries = None mediaLabel = None dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-speed=4", "-Z", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_011(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=None, imagePath=None, entries=, mediaLabel=None, dryRun=False. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = None imagePath = None entries = { "path1": None, } mediaLabel = None dryRun = False expected = [ "-use-the-force-luke=tty", "-M", "/dev/dvd", "-r", "-graft-points", "path1", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_012(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=None, imagePath=None, entries=, mediaLabel=None, dryRun=True. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = None imagePath = None entries = { "path1": None, } mediaLabel = None dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-M", "/dev/dvd", "-r", "-graft-points", "path1", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_013(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=None, imagePath=None, entries=, mediaLabel=None, dryRun=False. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = None imagePath = None entries = { "path1": None, "path2": "graft2", "path3": "/path/to/graft3", } mediaLabel = None dryRun = False expected = [ "-use-the-force-luke=tty", "-Z", "/dev/dvd", "-r", "-graft-points", "path1", "graft2/=path2", "path/to/graft3/=path3", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_014(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=None, imagePath=None, entries=, mediaLabel=None, dryRun=True. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = None imagePath = None entries = { "path1": None, "path2": "graft2", "path3": "/path/to/graft3", } mediaLabel = None dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-Z", "/dev/dvd", "-r", "-graft-points", "path1", "graft2/=path2", "path/to/graft3/=path3", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_015(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=1, imagePath=None, entries=, mediaLabel=None, dryRun=False. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = 1 imagePath = None entries = { "path1": None, "path2": "graft2", } mediaLabel = None dryRun = False expected = [ "-use-the-force-luke=tty", "-speed=1", "-M", "/dev/dvd", "-r", "-graft-points", "path1", "graft2/=path2", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_016(self): """ Test with newDisc=False, hardwareId="/dev/dvd", driveSpeed=2, imagePath=None, entries=, mediaLabel=None, dryRun=True. """ newDisc = False hardwareId = "/dev/dvd" driveSpeed = 2 imagePath = None entries = { "path1": None, "path2": "graft2", } mediaLabel = None dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-speed=2", "-M", "/dev/dvd", "-r", "-graft-points", "path1", "graft2/=path2", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_017(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=3, imagePath=None, entries=, mediaLabel=None, dryRun=False. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = 3 imagePath = None entries = { "path1": None, "/path/to/path2": None, "/path/to/path3/": "/path/to/graft3/", } mediaLabel = None dryRun = False expected = [ "-use-the-force-luke=tty", "-speed=3", "-Z", "/dev/dvd", "-r", "-graft-points", "/path/to/path2", "path/to/graft3/=/path/to/path3/", "path1", ] # sorted order actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_018(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=4, imagePath=None, entries=, mediaLabel=None, dryRun=True. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = 4 imagePath = None entries = { "path1": None, "/path/to/path2": None, "/path/to/path3/": "/path/to/graft3/", } mediaLabel = None dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-speed=4", "-Z", "/dev/dvd", "-r", "-graft-points", "/path/to/path2", "path/to/graft3/=/path/to/path3/", "path1", ] # sorted order actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_019(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=3, imagePath="/path/to/image", entries=None, mediaLabel="BACKUP", dryRun=False. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = 3 imagePath = "/path/to/image" entries = None mediaLabel = "BACKUP" dryRun = False expected = [ "-use-the-force-luke=tty", "-speed=3", "-Z", "/dev/dvd=/path/to/image", ] actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) def testBuildWriteArgs_020(self): """ Test with newDisc=True, hardwareId="/dev/dvd", driveSpeed=4, imagePath=None, entries=, mediaLabel="BACKUP", dryRun=True. """ newDisc = True hardwareId = "/dev/dvd" driveSpeed = 4 imagePath = None entries = { "path1": None, "/path/to/path2": None, "/path/to/path3/": "/path/to/graft3/", } mediaLabel = "BACKUP" dryRun = True expected = [ "-use-the-force-luke=tty", "-dry-run", "-speed=4", "-Z", "/dev/dvd", "-V", "BACKUP", "-r", "-graft-points", "/path/to/path2", "path/to/graft3/=/path/to/path3/", "path1", ] # sorted order actual = DvdWriter._buildWriteArgs(newDisc, hardwareId, driveSpeed, imagePath, entries, mediaLabel, dryRun) self.assertEqual(actual, expected) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1569562 cedar_backup3-3.8.1/tests/test_encrypt.py0000644000000000000000000015206314567004737015422 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests encrypt extension functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/extend/encrypt.py. Code Coverage ============= This module contains individual tests for the the public classes implemented in extend/encrypt.py. There are also tests for some of the private functions. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract a node, build some XML from it, and then feed that XML back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. Full vs. Reduced Tests ====================== Some Cedar Backup regression tests require a specialized environment in order to run successfully. This environment won't necessarily be available on every build system out there (for instance, on a Debian autobuilder). Because of this, the default behavior is to run a "reduced feature set" test suite that has no surprising system, kernel or network requirements. If you want to run all of the tests, set ENCRYPTTESTS_FULL to "Y" in the environment. In this module, the primary dependency is that for some tests, GPG must have access to the public key EFD75934. There is also an assumption that GPG does *not* have access to a public key for anyone named "Bogus J. User" (so we can test failure scenarios). @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import os import tempfile import unittest from CedarBackup3.extend.encrypt import EncryptConfig, LocalConfig, _encryptDailyDir, _encryptFile, _encryptFileWithGpg from CedarBackup3.filesystem import FilesystemList from CedarBackup3.testutil import buildPath, configureLogging, extractTar, failUnlessAssignRaises, findResources, removedir from CedarBackup3.xmlutil import createOutputDom, serializeDom ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "encrypt.conf.1", "encrypt.conf.2", "tree1.tar.gz", "tree2.tar.gz", "tree8.tar.gz", "tree15.tar.gz", "tree16.tar.gz", "tree17.tar.gz", "tree18.tar.gz", "tree19.tar.gz", "tree20.tar.gz", ] VALID_GPG_RECIPIENT = "EFD75934" INVALID_GPG_RECIPIENT = "Bogus J. User" INVALID_PATH = "bogus" # This path name should never exist ####################################################################### # Utility functions ####################################################################### def runAllTests(): """Returns true/false depending on whether the full test suite should be run.""" if "ENCRYPTTESTS_FULL" in os.environ: return os.environ["ENCRYPTTESTS_FULL"] == "Y" else: return False ####################################################################### # Test Case Classes ####################################################################### ########################## # TestEncryptConfig class ########################## class TestEncryptConfig(unittest.TestCase): """Tests for the EncryptConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = EncryptConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ encrypt = EncryptConfig() self.assertEqual(None, encrypt.encryptMode) self.assertEqual(None, encrypt.encryptTarget) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ encrypt = EncryptConfig("gpg", "Backup User") self.assertEqual("gpg", encrypt.encryptMode) self.assertEqual("Backup User", encrypt.encryptTarget) def testConstructor_003(self): """ Test assignment of encryptMode attribute, None value. """ encrypt = EncryptConfig(encryptMode="gpg") self.assertEqual("gpg", encrypt.encryptMode) encrypt.encryptMode = None self.assertEqual(None, encrypt.encryptMode) def testConstructor_004(self): """ Test assignment of encryptMode attribute, valid value. """ encrypt = EncryptConfig() self.assertEqual(None, encrypt.encryptMode) encrypt.encryptMode = "gpg" self.assertEqual("gpg", encrypt.encryptMode) def testConstructor_005(self): """ Test assignment of encryptMode attribute, invalid value (empty). """ encrypt = EncryptConfig() self.assertEqual(None, encrypt.encryptMode) self.failUnlessAssignRaises(ValueError, encrypt, "encryptMode", "") self.assertEqual(None, encrypt.encryptMode) def testConstructor_006(self): """ Test assignment of encryptTarget attribute, None value. """ encrypt = EncryptConfig(encryptTarget="Backup User") self.assertEqual("Backup User", encrypt.encryptTarget) encrypt.encryptTarget = None self.assertEqual(None, encrypt.encryptTarget) def testConstructor_007(self): """ Test assignment of encryptTarget attribute, valid value. """ encrypt = EncryptConfig() self.assertEqual(None, encrypt.encryptTarget) encrypt.encryptTarget = "Backup User" self.assertEqual("Backup User", encrypt.encryptTarget) def testConstructor_008(self): """ Test assignment of encryptTarget attribute, invalid value (empty). """ encrypt = EncryptConfig() self.assertEqual(None, encrypt.encryptTarget) self.failUnlessAssignRaises(ValueError, encrypt, "encryptTarget", "") self.assertEqual(None, encrypt.encryptTarget) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ encrypt1 = EncryptConfig() encrypt2 = EncryptConfig() self.assertEqual(encrypt1, encrypt2) self.assertTrue(encrypt1 == encrypt2) self.assertTrue(not encrypt1 < encrypt2) self.assertTrue(encrypt1 <= encrypt2) self.assertTrue(not encrypt1 > encrypt2) self.assertTrue(encrypt1 >= encrypt2) self.assertTrue(not encrypt1 != encrypt2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ encrypt1 = EncryptConfig("gpg", "Backup User") encrypt2 = EncryptConfig("gpg", "Backup User") self.assertEqual(encrypt1, encrypt2) self.assertTrue(encrypt1 == encrypt2) self.assertTrue(not encrypt1 < encrypt2) self.assertTrue(encrypt1 <= encrypt2) self.assertTrue(not encrypt1 > encrypt2) self.assertTrue(encrypt1 >= encrypt2) self.assertTrue(not encrypt1 != encrypt2) def testComparison_003(self): """ Test comparison of two differing objects, encryptMode differs (one None). """ encrypt1 = EncryptConfig() encrypt2 = EncryptConfig(encryptMode="gpg") self.assertNotEqual(encrypt1, encrypt2) self.assertTrue(not encrypt1 == encrypt2) self.assertTrue(encrypt1 < encrypt2) self.assertTrue(encrypt1 <= encrypt2) self.assertTrue(not encrypt1 > encrypt2) self.assertTrue(not encrypt1 >= encrypt2) self.assertTrue(encrypt1 != encrypt2) # Note: no test to check when encrypt mode differs, since only one value is allowed def testComparison_004(self): """ Test comparison of two differing objects, encryptTarget differs (one None). """ encrypt1 = EncryptConfig() encrypt2 = EncryptConfig(encryptTarget="Backup User") self.assertNotEqual(encrypt1, encrypt2) self.assertTrue(not encrypt1 == encrypt2) self.assertTrue(encrypt1 < encrypt2) self.assertTrue(encrypt1 <= encrypt2) self.assertTrue(not encrypt1 > encrypt2) self.assertTrue(not encrypt1 >= encrypt2) self.assertTrue(encrypt1 != encrypt2) def testComparison_005(self): """ Test comparison of two differing objects, encryptTarget differs. """ encrypt1 = EncryptConfig("gpg", "Another User") encrypt2 = EncryptConfig("gpg", "Backup User") self.assertNotEqual(encrypt1, encrypt2) self.assertTrue(not encrypt1 == encrypt2) self.assertTrue(encrypt1 < encrypt2) self.assertTrue(encrypt1 <= encrypt2) self.assertTrue(not encrypt1 > encrypt2) self.assertTrue(not encrypt1 >= encrypt2) self.assertTrue(encrypt1 != encrypt2) ######################## # TestLocalConfig class ######################## class TestLocalConfig(unittest.TestCase): """Tests for the LocalConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) def validateAddConfig(self, origConfig): """ Validates that document dumped from ``LocalConfig.addConfig`` results in identical object. We dump a document containing just the encrypt configuration, and then make sure that if we push that document back into the ``LocalConfig`` object, that the resulting object matches the original. The ``self.failUnlessEqual`` method is used for the validation, so if the method call returns normally, everything is OK. Args: origConfig: Original configuration """ (xmlDom, parentNode) = createOutputDom() origConfig.addConfig(xmlDom, parentNode) xmlData = serializeDom(xmlDom) newConfig = LocalConfig(xmlData=xmlData, validate=False) self.assertEqual(origConfig, newConfig) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalConfig() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = LocalConfig(validate=False) self.assertEqual(None, config.encrypt) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = LocalConfig(validate=True) self.assertEqual(None, config.encrypt) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["encrypt.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test assignment of encrypt attribute, None value. """ config = LocalConfig() config.encrypt = None self.assertEqual(None, config.encrypt) def testConstructor_005(self): """ Test assignment of encrypt attribute, valid value. """ config = LocalConfig() config.encrypt = EncryptConfig() self.assertEqual(EncryptConfig(), config.encrypt) def testConstructor_006(self): """ Test assignment of encrypt attribute, invalid value (not EncryptConfig). """ config = LocalConfig() self.failUnlessAssignRaises(ValueError, config, "encrypt", "STRING!") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = LocalConfig() config2 = LocalConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = LocalConfig() config1.encrypt = EncryptConfig() config2 = LocalConfig() config2.encrypt = EncryptConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, encrypt differs (one None). """ config1 = LocalConfig() config2 = LocalConfig() config2.encrypt = EncryptConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, encrypt differs. """ config1 = LocalConfig() config1.encrypt = EncryptConfig(encryptTarget="Another User") config2 = LocalConfig() config2.encrypt = EncryptConfig(encryptTarget="Backup User") self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on a None encrypt section. """ config = LocalConfig() config.encrypt = None self.assertRaises(ValueError, config.validate) def testValidate_002(self): """ Test validate on an empty encrypt section. """ config = LocalConfig() config.encrypt = EncryptConfig() self.assertRaises(ValueError, config.validate) def testValidate_003(self): """ Test validate on a non-empty encrypt section with no values filled in. """ config = LocalConfig() config.encrypt = EncryptConfig(None, None) self.assertRaises(ValueError, config.validate) def testValidate_004(self): """ Test validate on a non-empty encrypt section with only one value filled in. """ config = LocalConfig() config.encrypt = EncryptConfig("gpg", None) self.assertRaises(ValueError, config.validate) config.encrypt = EncryptConfig(None, "Backup User") self.assertRaises(ValueError, config.validate) def testValidate_005(self): """ Test validate on a non-empty encrypt section with valid values filled in. """ config = LocalConfig() config.encrypt = EncryptConfig("gpg", "Backup User") config.validate() ############################ # Test parsing of documents ############################ def testParse_001(self): """ Parse empty config document. """ path = self.resources["encrypt.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlPath=path, validate=True) self.assertRaises(ValueError, LocalConfig, xmlData=contents, validate=True) config = LocalConfig(xmlPath=path, validate=False) self.assertEqual(None, config.encrypt) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual(None, config.encrypt) def testParse_002(self): """ Parse config document with filled-in values. """ path = self.resources["encrypt.conf.2"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.encrypt) self.assertEqual("gpg", config.encrypt.encryptMode) self.assertEqual("Backup User", config.encrypt.encryptTarget) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.encrypt) self.assertEqual("gpg", config.encrypt.encryptMode) self.assertEqual("Backup User", config.encrypt.encryptTarget) ################### # Test addConfig() ################### def testAddConfig_001(self): """ Test with empty config document. """ encrypt = EncryptConfig() config = LocalConfig() config.encrypt = encrypt self.validateAddConfig(config) def testAddConfig_002(self): """ Test with values set. """ encrypt = EncryptConfig(encryptMode="gpg", encryptTarget="Backup User") config = LocalConfig() config.encrypt = encrypt self.validateAddConfig(config) ###################### # TestFunctions class ###################### @unittest.skipUnless(runAllTests(), "Limited test suite") class TestFunctions(unittest.TestCase): """Tests for the functions in encrypt.py.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) ############################# # Test _encryptFileWithGpg() ############################# def testEncryptFileWithGpg_001(self): """ Test for a non-existent file in a non-existent directory. """ sourceFile = self.buildPath([INVALID_PATH, INVALID_PATH]) self.assertRaises(IOError, _encryptFileWithGpg, sourceFile, INVALID_GPG_RECIPIENT) def testEncryptFileWithGpg_002(self): """ Test for a non-existent file in an existing directory. """ self.extractTar("tree8") sourceFile = self.buildPath(["tree8", "dir001", INVALID_PATH]) self.assertRaises(IOError, _encryptFileWithGpg, sourceFile, INVALID_GPG_RECIPIENT) def testEncryptFileWithGpg_003(self): """ Test for an unknown recipient. """ self.extractTar("tree1") sourceFile = self.buildPath(["tree1", "file001"]) expectedFile = self.buildPath(["tree1", "file001.gpg"]) self.assertRaises(IOError, _encryptFileWithGpg, sourceFile, INVALID_GPG_RECIPIENT) self.assertFalse(os.path.exists(expectedFile)) self.assertTrue(os.path.exists(sourceFile)) def testEncryptFileWithGpg_004(self): """ Test for a valid recipient. """ self.extractTar("tree1") sourceFile = self.buildPath(["tree1", "file001"]) expectedFile = self.buildPath(["tree1", "file001.gpg"]) actualFile = _encryptFileWithGpg(sourceFile, VALID_GPG_RECIPIENT) self.assertEqual(actualFile, expectedFile) self.assertTrue(os.path.exists(sourceFile)) self.assertTrue(os.path.exists(actualFile)) ###################### # Test _encryptFile() ###################### def testEncryptFile_001(self): """ Test for a mode other than "gpg". """ self.extractTar("tree1") sourceFile = self.buildPath(["tree1", "file001"]) expectedFile = self.buildPath(["tree1", "file001.gpg"]) self.assertRaises(ValueError, _encryptFile, sourceFile, "pgp", INVALID_GPG_RECIPIENT, None, None, removeSource=False) self.assertTrue(os.path.exists(sourceFile)) self.assertFalse(os.path.exists(expectedFile)) def testEncryptFile_002(self): """ Test for a source path that does not exist. """ self.extractTar("tree1") sourceFile = self.buildPath(["tree1", INVALID_PATH]) expectedFile = self.buildPath(["tree1", "%s.gpg" % INVALID_PATH]) self.assertRaises(ValueError, _encryptFile, sourceFile, "gpg", INVALID_GPG_RECIPIENT, None, None, removeSource=False) self.assertFalse(os.path.exists(sourceFile)) self.assertFalse(os.path.exists(expectedFile)) def testEncryptFile_003(self): """ Test "gpg" mode with a valid source path and invalid recipient, removeSource=False. """ self.extractTar("tree1") sourceFile = self.buildPath(["tree1", "file001"]) expectedFile = self.buildPath(["tree1", "file001.gpg"]) self.assertRaises(IOError, _encryptFile, sourceFile, "gpg", INVALID_GPG_RECIPIENT, None, None, removeSource=False) self.assertTrue(os.path.exists(sourceFile)) self.assertFalse(os.path.exists(expectedFile)) def testEncryptFile_004(self): """ Test "gpg" mode with a valid source path and invalid recipient, removeSource=True. """ self.extractTar("tree1") sourceFile = self.buildPath(["tree1", "file001"]) expectedFile = self.buildPath(["tree1", "file001.gpg"]) self.assertRaises(IOError, _encryptFile, sourceFile, "gpg", INVALID_GPG_RECIPIENT, None, None, removeSource=True) self.assertTrue(os.path.exists(sourceFile)) self.assertFalse(os.path.exists(expectedFile)) def testEncryptFile_005(self): """ Test "gpg" mode with a valid source path and recipient, removeSource=False. """ self.extractTar("tree1") sourceFile = self.buildPath(["tree1", "file001"]) expectedFile = self.buildPath(["tree1", "file001.gpg"]) actualFile = _encryptFile(sourceFile, "gpg", VALID_GPG_RECIPIENT, None, None, removeSource=False) self.assertEqual(actualFile, expectedFile) self.assertTrue(os.path.exists(sourceFile)) self.assertTrue(os.path.exists(actualFile)) def testEncryptFile_006(self): """ Test "gpg" mode with a valid source path and recipient, removeSource=True. """ self.extractTar("tree1") sourceFile = self.buildPath(["tree1", "file001"]) expectedFile = self.buildPath(["tree1", "file001.gpg"]) actualFile = _encryptFile(sourceFile, "gpg", VALID_GPG_RECIPIENT, None, None, removeSource=True) self.assertEqual(actualFile, expectedFile) self.assertFalse(os.path.exists(sourceFile)) self.assertTrue(os.path.exists(actualFile)) ########################## # Test _encryptDailyDir() ########################## def testEncryptDailyDir_001(self): """ Test with a nonexistent daily staging directory. """ self.extractTar("tree1") dailyDir = self.buildPath(["tree1", "dir001"]) self.assertRaises(ValueError, _encryptDailyDir, dailyDir, "gpg", VALID_GPG_RECIPIENT, None, None) def testEncryptDailyDir_002(self): """ Test with a valid staging directory containing only links. """ self.extractTar("tree15") dailyDir = self.buildPath(["tree15", "dir001"]) fsList = FilesystemList() fsList.addDirContents(dailyDir) self.assertEqual(3, len(fsList)) self.assertTrue(self.buildPath(["tree15", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree15", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree15", "dir001", "link002"]) in fsList) _encryptDailyDir(dailyDir, "gpg", VALID_GPG_RECIPIENT, None, None) fsList = FilesystemList() fsList.addDirContents(dailyDir) self.assertEqual(3, len(fsList)) self.assertTrue(self.buildPath(["tree15", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree15", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree15", "dir001", "link002"]) in fsList) def testEncryptDailyDir_003(self): """ Test with a valid staging directory containing only directories. """ self.extractTar("tree2") dailyDir = self.buildPath(["tree2"]) fsList = FilesystemList() fsList.addDirContents(dailyDir) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) _encryptDailyDir(dailyDir, "gpg", VALID_GPG_RECIPIENT, None, None) fsList = FilesystemList() fsList.addDirContents(dailyDir) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testEncryptDailyDir_004(self): """ Test with a valid staging directory containing only files. """ self.extractTar("tree1") dailyDir = self.buildPath(["tree1"]) fsList = FilesystemList() fsList.addDirContents(dailyDir) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) _encryptDailyDir(dailyDir, "gpg", VALID_GPG_RECIPIENT, None, None) fsList = FilesystemList() fsList.addDirContents(dailyDir) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007.gpg"]) in fsList) def testEncryptDailyDir_005(self): """ Test with a valid staging directory containing files, directories and links, including various files that match the general Cedar Backup indicator file pattern ("cback."). """ self.extractTar("tree16") dailyDir = self.buildPath(["tree16"]) fsList = FilesystemList() fsList.addDirContents(dailyDir) self.assertEqual(122, len(fsList)) self.assertTrue(self.buildPath(["tree16"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "cback."]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "cback.store"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "cback."]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "cback.collect"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "cback.store"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "cback.collect"]) in fsList) self.assertTrue(self.buildPath(["tree16", "cback.stage"]) in fsList) self.assertTrue(self.buildPath(["tree16", "cback.store"]) in fsList) _encryptDailyDir(dailyDir, "gpg", VALID_GPG_RECIPIENT, None, None) fsList = FilesystemList() fsList.addDirContents(dailyDir) # since all links are to files, and the files all changed names, the links are invalid and disappear self.assertEqual(102, len(fsList)) self.assertTrue(self.buildPath(["tree16"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file005.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file006.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file007.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir001", "file008.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir002", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir001", "dir003", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir001", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir002", "dir002", "file005.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file005.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file006.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file007.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "file008.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir001", "cback."]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir002", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir003", "dir003", "cback.store"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file005.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file006.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file007.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "file008.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "cback."]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir001", "cback.collect"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir002", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir003", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "cback.encrypt"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file005.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file006.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file007.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "file008.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir004", "cback.store"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file001.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file002.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file003.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file004.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file005.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file006.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file007.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "dir004", "dir005", "file008.gpg"]) in fsList) self.assertTrue(self.buildPath(["tree16", "cback.collect"]) in fsList) self.assertTrue(self.buildPath(["tree16", "cback.stage"]) in fsList) self.assertTrue(self.buildPath(["tree16", "cback.store"]) in fsList) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1609564 cedar_backup3-3.8.1/tests/test_filesystem.py0000644000000000000000000473332614567004737016135 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests filesystem-related classes. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/filesystem.py. Test Notes ========== This module contains individual tests for each of the classes implemented in filesystem.py: FilesystemList, BackupFileList and PurgeItemList. The BackupFileList and PurgeItemList classes inherit from FilesystemList, and the FilesystemList class itself inherits from the standard Python list class. For the most part, I won't spend time testing inherited functionality, especially if it's already been tested. However, I do test some of the base list functionality just to ensure that the inheritence has been constructed properly and everything seems to work as expected. You may look at this code and ask, "Why all of the checks that XXX is in list YYY? Why not just compare what we got to a known list?" The answer is that the order of the list is not significant, only its contents. We can't be positive about the order in which we recurse a directory, but we do need to make sure that everything we expect is in the list and nothing more. We do this by checking the count if items and then making sure that exactly that many known items exist in the list. This file is ridiculously long, almost too long to be worked with easily. I really should split it up into smaller files, but I like having a 1:1 relationship between a module and its test. These tests do pass on Windows, but I've taken the easy way out. In most cases, rather than establising new expected results, I've excluded the tests that require soft links or other UNIX-specific behaviors. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality. Instead, I create lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_023``. Each method then has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge the extent of a problem when one exists. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a FILESYSTEMTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import hashlib import os import tarfile import tempfile import unittest from CedarBackup3.filesystem import BackupFileList, FilesystemList, PurgeItemList, compareContents, normalizeDir from CedarBackup3.testutil import ( buildPath, changeFileAge, configureLogging, extractTar, failUnlessAssignRaises, findResources, platformMacOsX, platformSupportsLinks, randomFilename, removedir, ) from CedarBackup3.util import encodePath, pathJoin ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = ["./data", "./tests/data"] RESOURCES = [ "tree1.tar.gz", "tree2.tar.gz", "tree3.tar.gz", "tree4.tar.gz", "tree5.tar.gz", "tree6.tar.gz", "tree7.tar.gz", "tree8.tar.gz", "tree9.tar.gz", "tree10.tar.gz", "tree11.tar.gz", "tree12.tar.gz", "tree13.tar.gz", "tree22.tar.gz", ] INVALID_FILE = "bogus" # This file name should never exist NOMATCH_PATH = "/something" # This path should never match something we put in a file list NOMATCH_BASENAME = "something" # This basename should never match something we put in a file list NOMATCH_PATTERN = "pattern" # This pattern should never match something we put in a file list AGE_1_HOUR = 1 * 60 * 60 # in seconds AGE_2_HOURS = 2 * 60 * 60 # in seconds AGE_12_HOURS = 12 * 60 * 60 # in seconds AGE_23_HOURS = 23 * 60 * 60 # in seconds AGE_24_HOURS = 24 * 60 * 60 # in seconds AGE_25_HOURS = 25 * 60 * 60 # in seconds AGE_47_HOURS = 47 * 60 * 60 # in seconds AGE_48_HOURS = 48 * 60 * 60 # in seconds AGE_49_HOURS = 49 * 60 * 60 # in seconds ####################################################################### # Test Case Classes ####################################################################### ########################### # TestFilesystemList class ########################### class TestFilesystemList(unittest.TestCase): """Tests for the FilesystemList class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) def pathPattern(self, path): """Returns properly-escaped regular expression pattern matching the indicated path.""" return ".*%s.*" % path.replace("\\", "\\\\") def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test attribute assignment ############################ def testAssignment_001(self): """ Test assignment of excludeFiles attribute, true values. """ fsList = FilesystemList() self.assertEqual(False, fsList.excludeFiles) fsList.excludeFiles = True self.assertEqual(True, fsList.excludeFiles) fsList.excludeFiles = [ 1, ] self.assertEqual(True, fsList.excludeFiles) def testAssignment_002(self): """ Test assignment of excludeFiles attribute, false values. """ fsList = FilesystemList() self.assertEqual(False, fsList.excludeFiles) fsList.excludeFiles = False self.assertEqual(False, fsList.excludeFiles) fsList.excludeFiles = [] self.assertEqual(False, fsList.excludeFiles) def testAssignment_003(self): """ Test assignment of excludeLinks attribute, true values. """ fsList = FilesystemList() self.assertEqual(False, fsList.excludeLinks) fsList.excludeLinks = True self.assertEqual(True, fsList.excludeLinks) fsList.excludeLinks = [ 1, ] self.assertEqual(True, fsList.excludeLinks) def testAssignment_004(self): """ Test assignment of excludeLinks attribute, false values. """ fsList = FilesystemList() self.assertEqual(False, fsList.excludeLinks) fsList.excludeLinks = False self.assertEqual(False, fsList.excludeLinks) fsList.excludeLinks = [] self.assertEqual(False, fsList.excludeLinks) def testAssignment_005(self): """ Test assignment of excludeDirs attribute, true values. """ fsList = FilesystemList() self.assertEqual(False, fsList.excludeDirs) fsList.excludeDirs = True self.assertEqual(True, fsList.excludeDirs) fsList.excludeDirs = [ 1, ] self.assertEqual(True, fsList.excludeDirs) def testAssignment_006(self): """ Test assignment of excludeDirs attribute, false values. """ fsList = FilesystemList() self.assertEqual(False, fsList.excludeDirs) fsList.excludeDirs = False self.assertEqual(False, fsList.excludeDirs) fsList.excludeDirs = [] self.assertEqual(False, fsList.excludeDirs) def testAssignment_007(self): """ Test assignment of ignoreFile attribute. """ fsList = FilesystemList() self.assertEqual(None, fsList.ignoreFile) fsList.ignoreFile = "ken" self.assertEqual("ken", fsList.ignoreFile) fsList.ignoreFile = None self.assertEqual(None, fsList.ignoreFile) def testAssignment_008(self): """ Test assignment of excludePaths attribute. """ fsList = FilesystemList() self.assertEqual([], fsList.excludePaths) fsList.excludePaths = None self.assertEqual([], fsList.excludePaths) fsList.excludePaths = [ "/path/to/something/absolute", ] self.assertEqual(["/path/to/something/absolute"], fsList.excludePaths) fsList.excludePaths = [ "/path/to/something/absolute", "/path/to/something/else", ] self.assertEqual(["/path/to/something/absolute", "/path/to/something/else"], fsList.excludePaths) self.failUnlessAssignRaises(ValueError, fsList, "excludePaths", ["path/to/something/relative"]) self.failUnlessAssignRaises( ValueError, fsList, "excludePaths", ["/path/to/something/absolute", "path/to/something/relative"] ) fsList.excludePaths = [ "/path/to/something/absolute", ] self.assertEqual(["/path/to/something/absolute"], fsList.excludePaths) fsList.excludePaths.insert(0, "/ken") self.assertEqual(["/ken", "/path/to/something/absolute"], fsList.excludePaths) fsList.excludePaths.append("/file") self.assertEqual(["/ken", "/path/to/something/absolute", "/file"], fsList.excludePaths) fsList.excludePaths.extend(["/one", "/two"]) self.assertEqual(["/ken", "/path/to/something/absolute", "/file", "/one", "/two"], fsList.excludePaths) fsList.excludePaths = [ "/path/to/something/absolute", ] self.assertRaises(ValueError, fsList.excludePaths.insert, 0, "path/to/something/relative") self.assertRaises(ValueError, fsList.excludePaths.append, "path/to/something/relative") self.assertRaises(ValueError, fsList.excludePaths.extend, ["path/to/something/relative"]) def testAssignment_009(self): """ Test assignment of excludePatterns attribute. """ fsList = FilesystemList() self.assertEqual([], fsList.excludePatterns) fsList.excludePatterns = None self.assertEqual([], fsList.excludePatterns) fsList.excludePatterns = [ r".*\.jpg", ] self.assertEqual([r".*\.jpg"], fsList.excludePatterns) fsList.excludePatterns = [ r".*\.jpg", "[a-zA-Z0-9]*", ] self.assertEqual([r".*\.jpg", "[a-zA-Z0-9]*"], fsList.excludePatterns) self.failUnlessAssignRaises(ValueError, fsList, "excludePatterns", ["*.jpg"]) self.failUnlessAssignRaises(ValueError, fsList, "excludePatterns", ["*.jpg", "[a-zA-Z0-9]*"]) fsList.excludePatterns = [ r".*\.jpg", ] self.assertEqual([r".*\.jpg"], fsList.excludePatterns) fsList.excludePatterns.insert(0, "ken") self.assertEqual(["ken", r".*\.jpg"], fsList.excludePatterns) fsList.excludePatterns.append("pattern") self.assertEqual(["ken", r".*\.jpg", "pattern"], fsList.excludePatterns) fsList.excludePatterns.extend(["one", "two"]) self.assertEqual(["ken", r".*\.jpg", "pattern", "one", "two"], fsList.excludePatterns) fsList.excludePatterns = [ r".*\.jpg", ] self.assertRaises(ValueError, fsList.excludePatterns.insert, 0, "*.jpg") self.assertEqual([r".*\.jpg"], fsList.excludePatterns) self.assertRaises(ValueError, fsList.excludePatterns.append, "*.jpg") self.assertEqual([r".*\.jpg"], fsList.excludePatterns) self.assertRaises(ValueError, fsList.excludePatterns.extend, ["*.jpg"]) self.assertEqual([r".*\.jpg"], fsList.excludePatterns) def testAssignment_010(self): """ Test assignment of excludeBasenamePatterns attribute. """ fsList = FilesystemList() self.assertEqual([], fsList.excludeBasenamePatterns) fsList.excludeBasenamePatterns = None self.assertEqual([], fsList.excludeBasenamePatterns) fsList.excludeBasenamePatterns = [ r".*\.jpg", ] self.assertEqual([r".*\.jpg"], fsList.excludeBasenamePatterns) fsList.excludeBasenamePatterns = [ r".*\.jpg", "[a-zA-Z0-9]*", ] self.assertEqual([r".*\.jpg", "[a-zA-Z0-9]*"], fsList.excludeBasenamePatterns) self.failUnlessAssignRaises(ValueError, fsList, "excludeBasenamePatterns", ["*.jpg"]) self.failUnlessAssignRaises(ValueError, fsList, "excludeBasenamePatterns", ["*.jpg", "[a-zA-Z0-9]*"]) fsList.excludeBasenamePatterns = [ r".*\.jpg", ] self.assertEqual([r".*\.jpg"], fsList.excludeBasenamePatterns) fsList.excludeBasenamePatterns.insert(0, "ken") self.assertEqual(["ken", r".*\.jpg"], fsList.excludeBasenamePatterns) fsList.excludeBasenamePatterns.append("pattern") self.assertEqual(["ken", r".*\.jpg", "pattern"], fsList.excludeBasenamePatterns) fsList.excludeBasenamePatterns.extend(["one", "two"]) self.assertEqual(["ken", r".*\.jpg", "pattern", "one", "two"], fsList.excludeBasenamePatterns) fsList.excludeBasenamePatterns = [ r".*\.jpg", ] self.assertRaises(ValueError, fsList.excludeBasenamePatterns.insert, 0, "*.jpg") self.assertEqual([r".*\.jpg"], fsList.excludeBasenamePatterns) self.assertRaises(ValueError, fsList.excludeBasenamePatterns.append, "*.jpg") self.assertEqual([r".*\.jpg"], fsList.excludeBasenamePatterns) self.assertRaises(ValueError, fsList.excludeBasenamePatterns.extend, ["*.jpg"]) self.assertEqual([r".*\.jpg"], fsList.excludeBasenamePatterns) ################################ # Test basic list functionality ################################ def testBasic_001(self): """ Test the append() method. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.append("a") self.assertEqual(["a"], fsList) fsList.append("b") self.assertEqual(["a", "b"], fsList) def testBasic_002(self): """ Test the insert() method. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.insert(0, "a") self.assertEqual(["a"], fsList) fsList.insert(0, "b") self.assertEqual(["b", "a"], fsList) def testBasic_003(self): """ Test the remove() method. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.insert(0, "a") fsList.insert(0, "b") self.assertEqual(["b", "a"], fsList) fsList.remove("a") self.assertEqual(["b"], fsList) fsList.remove("b") self.assertEqual([], fsList) def testBasic_004(self): """ Test the pop() method. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.append("a") fsList.append("b") fsList.append("c") fsList.append("d") fsList.append("e") self.assertEqual(["a", "b", "c", "d", "e"], fsList) self.assertEqual("e", fsList.pop()) self.assertEqual(["a", "b", "c", "d"], fsList) self.assertEqual("d", fsList.pop()) self.assertEqual(["a", "b", "c"], fsList) self.assertEqual("c", fsList.pop()) self.assertEqual(["a", "b"], fsList) self.assertEqual("b", fsList.pop()) self.assertEqual(["a"], fsList) self.assertEqual("a", fsList.pop()) self.assertEqual([], fsList) def testBasic_005(self): """ Test the count() method. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.append("a") fsList.append("b") fsList.append("c") fsList.append("d") fsList.append("e") self.assertEqual(["a", "b", "c", "d", "e"], fsList) self.assertEqual(1, fsList.count("a")) def testBasic_006(self): """ Test the index() method. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.append("a") fsList.append("b") fsList.append("c") fsList.append("d") fsList.append("e") self.assertEqual(["a", "b", "c", "d", "e"], fsList) self.assertEqual(2, fsList.index("c")) def testBasic_007(self): """ Test the reverse() method. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.append("a") fsList.append("b") fsList.append("c") fsList.append("d") fsList.append("e") self.assertEqual(["a", "b", "c", "d", "e"], fsList) fsList.reverse() self.assertEqual(["e", "d", "c", "b", "a"], fsList) fsList.reverse() self.assertEqual(["a", "b", "c", "d", "e"], fsList) def testBasic_008(self): """ Test the sort() method. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.append("e") fsList.append("d") fsList.append("c") fsList.append("b") fsList.append("a") self.assertEqual(["e", "d", "c", "b", "a"], fsList) fsList.sort() self.assertEqual(["a", "b", "c", "d", "e"], fsList) fsList.sort() self.assertEqual(["a", "b", "c", "d", "e"], fsList) def testBasic_009(self): """ Test slicing. """ fsList = FilesystemList() self.assertEqual([], fsList) fsList.append("e") fsList.append("d") fsList.append("c") fsList.append("b") fsList.append("a") self.assertEqual(["e", "d", "c", "b", "a"], fsList) self.assertEqual(["e", "d", "c", "b", "a"], fsList[:]) self.assertEqual(["e", "d", "c", "b", "a"], fsList[0:]) self.assertEqual("e", fsList[0]) self.assertEqual("a", fsList[4]) self.assertEqual(["d", "c", "b"], fsList[1:4]) ################# # Test addFile() ################# def testAddFile_001(self): """ Attempt to add a file that doesn't exist; no exclusions. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_002(self): """ Attempt to add a directory; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_003(self): """ Attempt to add a soft link; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_004(self): """ Attempt to add an existing file; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddFile_005(self): """ Attempt to add a file that doesn't exist; excludeFiles set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_006(self): """ Attempt to add a directory; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_007(self): """ Attempt to add a soft link; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_008(self): """ Attempt to add an existing file; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddFile_009(self): """ Attempt to add a file that doesn't exist; excludeDirs set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_010(self): """ Attempt to add a directory; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_011(self): """ Attempt to add a soft link; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_012(self): """ Attempt to add an existing file; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddFile_013(self): """ Attempt to add a file that doesn't exist; excludeLinks set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_014(self): """ Attempt to add a directory; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addFile, path) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddFile_015(self): """ Attempt to add a soft link; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_016(self): """ Attempt to add an existing file; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddFile_017(self): """ Attempt to add a file that doesn't exist; with excludePaths including the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_018(self): """ Attempt to add a directory; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_019(self): """ Attempt to add a soft link; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePaths = [path] count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_020(self): """ Attempt to add an existing file; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePaths = [path] count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddFile_021(self): """ Attempt to add a file that doesn't exist; with excludePaths not including the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_022(self): """ Attempt to add a directory; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_023(self): """ Attempt to add a soft link; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_024(self): """ Attempt to add an existing file; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddFile_025(self): """ Attempt to add a file that doesn't exist; with excludePatterns matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_026(self): """ Attempt to add a directory; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_027(self): """ Attempt to add a soft link; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_028(self): """ Attempt to add an existing file; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddFile_029(self): """ Attempt to add a file that doesn't exist; with excludePatterns not matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_030(self): """ Attempt to add a directory; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_031(self): """ Attempt to add a soft link; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_032(self): """ Attempt to add an existing file; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddFile_033(self): """ Attempt to add an invalid link (i.e. a link that points to something that doesn't exist). """ self.extractTar("tree10") path = self.buildPath(["tree10", "link001"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_034(self): """ Attempt to add a file that has spaces in its name. """ self.extractTar("tree11") path = self.buildPath(["tree11", "file with spaces"]) fsList = FilesystemList() count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddFile_035(self): """ Attempt to add a UTF-8 file. """ self.extractTar("tree12") path = self.buildPath(["tree12", "unicode", encodePath(b"\xe2\x99\xaa\xe2\x99\xac")]) fsList = FilesystemList() count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddFile_036(self): """ Attempt to add a file that doesn't exist; with excludeBasenamePatterns matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [INVALID_FILE] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_037(self): """ Attempt to add a directory; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "dir001", ] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_038(self): """ Attempt to add a soft link; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "link001", ] count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "link001", ] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_039(self): """ Attempt to add an existing file; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "file001", ] count = fsList.addFile(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddFile_040(self): """ Attempt to add a file that doesn't exist; with excludeBasenamePatterns not matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_041(self): """ Attempt to add a directory; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_042(self): """ Attempt to add a soft link; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeBasenamePaths = [NOMATCH_BASENAME] count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeBasenamePaths = [NOMATCH_BASENAME] self.assertRaises(ValueError, fsList.addFile, path) def testAddFile_043(self): """ Attempt to add an existing file; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = fsList.addFile(path) self.assertEqual(1, count) self.assertEqual([path], fsList) ################ # Test addDir() ################ def testAddDir_001(self): """ Attempt to add a directory that doesn't exist; no exclusions. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_002(self): """ Attempt to add a file; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_003(self): """ Attempt to add a soft link; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_004(self): """ Attempt to add an existing directory; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_005(self): """ Attempt to add a directory that doesn't exist; excludeFiles set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_006(self): """ Attempt to add a file; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_007(self): """ Attempt to add a soft link; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_008(self): """ Attempt to add an existing directory; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_009(self): """ Attempt to add a directory that doesn't exist; excludeDirs set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_010(self): """ Attempt to add a file; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_011(self): """ Attempt to add a soft link; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_012(self): """ Attempt to add an existing directory; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_013(self): """ Attempt to add a directory that doesn't exist; excludeLinks set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_014(self): """ Attempt to add a file; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addDir, path) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDir_015(self): """ Attempt to add a soft link; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_016(self): """ Attempt to add an existing directory; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_017(self): """ Attempt to add a directory that doesn't exist; with excludePaths including the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_018(self): """ Attempt to add a file; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_019(self): """ Attempt to add a soft link; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePaths = [path] count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_020(self): """ Attempt to add an existing directory; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePaths = [path] count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_021(self): """ Attempt to add a directory that doesn't exist; with excludePaths not including the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_022(self): """ Attempt to add a file; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_023(self): """ Attempt to add a soft link; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_024(self): """ Attempt to add an existing directory; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_025(self): """ Attempt to add a directory that doesn't exist; with excludePatterns matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_026(self): """ Attempt to add a file; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_027(self): """ Attempt to add a soft link; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_028(self): """ Attempt to add an existing directory; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_029(self): """ Attempt to add a directory that doesn't exist; with excludePatterns not matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_030(self): """ Attempt to add a file; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_031(self): """ Attempt to add a soft link; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_032(self): """ Attempt to add an existing directory; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_033(self): """ Attempt to add an invalid link (i.e. a link that points to something that doesn't exist). """ self.extractTar("tree10") path = self.buildPath(["tree10", "link001"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_034(self): """ Attempt to add a directory that has spaces in its name. """ self.extractTar("tree11") path = self.buildPath(["tree11", "dir with spaces"]) fsList = FilesystemList() count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_035(self): """ Attempt to add a directory that doesn't exist; with excludeBasenamePatterns matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [INVALID_FILE] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_036(self): """ Attempt to add a file; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "file001", ] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_037(self): """ Attempt to add a soft link; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "link001", ] self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "link001", ] count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_038(self): """ Attempt to add an existing directory; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "dir001", ] count = fsList.addDir(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDir_039(self): """ Attempt to add a directory that doesn't exist; with excludeBasenamePatterns not matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_040(self): """ Attempt to add a file; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, fsList.addDir, path) def testAddDir_041(self): """ Attempt to add a soft link; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, fsList.addDir, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDir_042(self): """ Attempt to add an existing directory; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) ######################## # Test addDirContents() ######################## def testAddDirContents_001(self): """ Attempt to add a directory that doesn't exist; no exclusions. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_002(self): """ Attempt to add a file; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_003(self): """ Attempt to add a soft link; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() count = fsList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_004(self): """ Attempt to add an empty directory containing ignore file; no exclusions. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_005(self): """ Attempt to add an empty directory; no exclusions. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_006(self): """ Attempt to add an non-empty directory containing ignore file; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_007(self): """ Attempt to add an non-empty directory; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree5", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in fsList) def testAddDirContents_008(self): """ Attempt to add a directory that doesn't exist; excludeFiles set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_009(self): """ Attempt to add a file; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_010(self): """ Attempt to add a soft link; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_011(self): """ Attempt to add an empty directory containing ignore file; excludeFiles set. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeFiles = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_012(self): """ Attempt to add an empty directory; excludeFiles set. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_013(self): """ Attempt to add an non-empty directory containing ignore file; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeFiles = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_014(self): """ Attempt to add an non-empty directory; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addDirContents(path) self.assertEqual(5, count) self.assertEqual(5, len(fsList)) self.assertTrue(self.buildPath(["tree5", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in fsList) def testAddDirContents_015(self): """ Attempt to add a directory that doesn't exist; excludeDirs set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_016(self): """ Attempt to add a file; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_017(self): """ Attempt to add a soft link; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_018(self): """ Attempt to add an empty directory containing ignore file; excludeDirs set. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeDirs = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_019(self): """ Attempt to add an empty directory; excludeDirs set. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_020(self): """ Attempt to add an non-empty directory containing ignore file; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeDirs = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_021(self): """ Attempt to add an non-empty directory; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addDirContents(path) self.assertEqual(3, count) self.assertEqual(3, len(fsList)) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in fsList) def testAddDirContents_023(self): """ Attempt to add a directory that doesn't exist; excludeLinks set. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_024(self): """ Attempt to add a file; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_025(self): """ Attempt to add a soft link; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addDirContents, path) if platformSupportsLinks(): path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_026(self): """ Attempt to add an empty directory containing ignore file; excludeLinks set. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeLinks = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_027(self): """ Attempt to add an empty directory; excludeLinks set. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertTrue(self.buildPath(["tree8", "dir001"]) in fsList) def testAddDirContents_028(self): """ Attempt to add an non-empty directory containing ignore file; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeLinks = True count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_029(self): """ Attempt to add an non-empty directory; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addDirContents(path) self.assertEqual(7, count) self.assertEqual(7, len(fsList)) self.assertTrue(self.buildPath(["tree5", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in fsList) def testAddDirContents_030(self): """ Attempt to add a directory that doesn't exist; with excludePaths including the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_031(self): """ Attempt to add a file; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_032(self): """ Attempt to add a soft link; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePaths = [path] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_033(self): """ Attempt to add an empty directory containing ignore file; with excludePaths including the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludePaths = [path] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_034(self): """ Attempt to add an empty directory; with excludePaths including the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludePaths = [path] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_035(self): """ Attempt to add an non-empty directory containing ignore file; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludePaths = [path] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_036(self): """ Attempt to add an non-empty directory; with excludePaths including the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePaths = [path] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_037(self): """ Attempt to add a directory that doesn't exist; with excludePaths not including the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_038(self): """ Attempt to add a file; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_039(self): """ Attempt to add a soft link; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_040(self): """ Attempt to add an empty directory containing ignore file; with excludePaths not including the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludePaths = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_041(self): """ Attempt to add an empty directory; with excludePaths not including the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_042(self): """ Attempt to add an non-empty directory containing ignore file; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludePaths = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_043(self): """ Attempt to add an non-empty directory; with excludePaths not including the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePaths = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree5", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in fsList) def testAddDirContents_044(self): """ Attempt to add a directory that doesn't exist; with excludePatterns matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_045(self): """ Attempt to add a file; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_046(self): """ Attempt to add a soft link; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_047(self): """ Attempt to add an empty directory containing ignore file; with excludePatterns matching the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_048(self): """ Attempt to add an empty directory; with excludePatterns matching the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_049(self): """ Attempt to add an non-empty directory containing ignore file; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_050(self): """ Attempt to add an non-empty directory; with excludePatterns matching the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_051(self): """ Attempt to add a directory that doesn't exist; with excludePatterns not matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_052(self): """ Attempt to add a file; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_053(self): """ Attempt to add a soft link; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_054(self): """ Attempt to add an empty directory containing ignore file; with excludePatterns not matching the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludePatterns = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_055(self): """ Attempt to add an empty directory; with excludePatterns not matching the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_056(self): """ Attempt to add an non-empty directory containing ignore file; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludePatterns = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_057(self): """ Attempt to add an non-empty directory; with excludePatterns not matching the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludePatterns = [NOMATCH_PATH] count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree5", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in fsList) def testAddDirContents_058(self): """ Attempt to add a large tree with no exclusions. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(136, count) self.assertEqual(136, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) def testAddDirContents_059(self): """ Attempt to add a large tree, with excludeFiles set. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addDirContents(path) self.assertEqual(42, count) self.assertEqual(42, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) def testAddDirContents_060(self): """ Attempt to add a large tree, with excludeDirs set. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addDirContents(path) self.assertEqual(94, count) self.assertEqual(94, len(fsList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_061(self): """ Attempt to add a large tree, with excludeLinks set. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addDirContents(path) self.assertEqual(96, count) self.assertEqual(96, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) def testAddDirContents_062(self): """ Attempt to add a large tree, with excludePaths set to exclude some entries. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() fsList.excludePaths = [ self.buildPath(["tree6", "dir001", "dir002"]), self.buildPath(["tree6", "dir002", "dir001", "dir001"]), self.buildPath(["tree6", "dir003", "dir002", "file001"]), self.buildPath(["tree6", "dir003", "dir002", "file002"]), ] count = fsList.addDirContents(path) self.assertEqual(125, count) self.assertEqual(125, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_063(self): """ Attempt to add a large tree, with excludePatterns set to exclude some entries. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() fsList.excludePatterns = [".*file001.*", r".*tree6\/dir002\/dir001.*"] count = fsList.addDirContents(path) self.assertEqual(108, count) self.assertEqual(108, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) def testAddDirContents_064(self): """ Attempt to add a large tree, with ignoreFile set to exclude some directories. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" count = fsList.addDirContents(path) self.assertEqual(79, count) self.assertEqual(79, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) def testAddDirContents_065(self): """ Attempt to add a link to a file. """ self.extractTar("tree9") path = self.buildPath(["tree9", "dir002", "link003"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDirContents, path) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_066(self): """ Attempt to add a link to a directory (which should add its contents). """ self.extractTar("tree9") path = self.buildPath(["tree9", "link002"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(9, count) self.assertEqual(9, len(fsList)) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "link004"]) in fsList) def testAddDirContents_067(self): """ Attempt to add an invalid link (i.e. a link that points to something that doesn't exist). """ self.extractTar("tree10") path = self.buildPath(["tree10", "link001"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_068(self): """ Attempt to add directory containing an invalid link (i.e. a link that points to something that doesn't exist). """ self.extractTar("tree10") path = self.buildPath(["tree10"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(3, count) self.assertEqual(3, len(fsList)) self.assertTrue(self.buildPath(["tree10"]) in fsList) self.assertTrue(self.buildPath(["tree10", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree10", "dir002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_069(self): """ Attempt to add a directory containing items with spaces. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testAddDirContents_070(self): """ Attempt to add a directory which has a name containing spaces. """ self.extractTar("tree11") path = self.buildPath(["tree11", "dir with spaces"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(5, count) self.assertEqual(5, len(fsList)) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testAddDirContents_071(self): """ Attempt to add a directory which has a UTF-8 filename in it. """ self.extractTar("tree12") path = self.buildPath(["tree12", "unicode"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(6, count) self.assertEqual(6, len(fsList)) self.assertTrue(self.buildPath(["tree12", "unicode"]) in fsList) self.assertTrue(self.buildPath(["tree12", "unicode", "README.strange-name"]) in fsList) self.assertTrue(self.buildPath(["tree12", "unicode", "utflist.long.gz"]) in fsList) self.assertTrue(self.buildPath(["tree12", "unicode", "utflist.cp437.gz"]) in fsList) self.assertTrue(self.buildPath(["tree12", "unicode", "utflist.short.gz"]) in fsList) self.assertTrue(self.buildPath(["tree12", "unicode", encodePath(b"\xe2\x99\xaa\xe2\x99\xac")]) in fsList) def testAddDirContents_072(self): """ Attempt to add a directory which has several UTF-8 filenames in it. This test data was taken from Rick Lowe's problems around the release of v1.10. I don't run the test for Darwin (Mac OS X) because the tarball isn't valid on that platform. All of the tests with unicode paths were incredibly painful to get working with Python 3, but these tests in particular were difficult, because character 0x82 is not a valid UTF-8 character. The key is was to get the filename into the same encoding used by methods like os.listdir(), which uses a "surrogateescape" fallback for encoding filenames. Once I switched encodePath to do the same thing, this test started passing. There's apparently no other way to represent filenames like this. """ if not platformMacOsX(): self.extractTar("tree13") path = self.buildPath(["tree13"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree13"]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"Les mouvements de r\x82forme.doc")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l'\x82nonc\x82.sxw")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l\x82onard - renvois et bibliographie.sxw")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l\x82onard copie finale.sxw")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l\x82onard de vinci - page titre.sxw")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l\x82onard de vinci.sxw")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"Rammstein - B\x81ck Dich.mp3")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"megaherz - Glas Und Tr\x84nen.mp3")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"Megaherz - Mistst\x81ck.MP3")]) in fsList) self.assertTrue(self.buildPath(["tree13", encodePath(b"Rammstein - Mutter - B\x94se.mp3")]) in fsList) def testAddDirContents_073(self): """ Attempt to add a large tree with recursive=False. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() count = fsList.addDirContents(path, recursive=False) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) def testAddDirContents_074(self): """ Attempt to add a directory that doesn't exist; with excludeBasenamePatterns matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [INVALID_FILE] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_075(self): """ Attempt to add a file; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "file001", ] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_076(self): """ Attempt to add a soft link; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "link001", ] self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "link001", ] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_077(self): """ Attempt to add an empty directory containing ignore file; with excludeBasenamePatterns matching the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeBasenamePatterns = [ "dir001", ] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_078(self): """ Attempt to add an empty directory; with excludeBasenamePatterns matching the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "dir001", ] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_079(self): """ Attempt to add an non-empty directory containing ignore file; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeBasenamePatterns = [ "dir008", ] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_080(self): """ Attempt to add an non-empty directory; with excludeBasenamePatterns matching the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "dir001", ] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_081(self): """ Attempt to add a directory that doesn't exist; with excludeBasenamePatterns not matching the path. """ path = self.buildPath([INVALID_FILE]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_082(self): """ Attempt to add a file; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, fsList.addDirContents, path) def testAddDirContents_083(self): """ Attempt to add a soft link; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, fsList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_084(self): """ Attempt to add an empty directory containing ignore file; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_085(self): """ Attempt to add an empty directory; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_086(self): """ Attempt to add an non-empty directory containing ignore file; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) fsList = FilesystemList() fsList.ignoreFile = "ignore" fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = fsList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_087(self): """ Attempt to add an non-empty directory; with excludeBasenamePatterns not matching the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree5", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in fsList) def testAddDirContents_088(self): """ Attempt to add a large tree, with excludeBasenamePatterns set to exclude some entries. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() fsList.excludeBasenamePatterns = ["file001", "dir001"] count = fsList.addDirContents(path) self.assertEqual(64, count) self.assertEqual(64, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) def testAddDirContents_089(self): """ Attempt to add a large tree with no exclusions, addSelf=True. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() count = fsList.addDirContents(path, addSelf=True) self.assertEqual(136, count) self.assertEqual(136, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) def testAddDirContents_090(self): """ Attempt to add a large tree with no exclusions, addSelf=False. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() count = fsList.addDirContents(path, addSelf=False) self.assertEqual(135, count) self.assertEqual(135, len(fsList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_091(self): """ Attempt to add a directory with linkDepth=1. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=1) self.assertEqual(165, count) self.assertEqual(165, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_092(self): """ Attempt to add a directory with linkDepth=2. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=2) self.assertEqual(241, count) self.assertEqual(241, len(fsList)) self.assertTrue(self.buildPath(["tree6"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "ignore"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree6", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree6", "link001"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_093(self): """ Attempt to add a directory with linkDepth=0, dereference=False. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=0, dereference=False) self.assertEqual(12, count) self.assertEqual(12, len(fsList)) self.assertTrue(self.buildPath(["tree22", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_094(self): """ Attempt to add a directory with linkDepth=1, dereference=False. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=1, dereference=False) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree22", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_095(self): """ Attempt to add a directory with linkDepth=2, dereference=False. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=2, dereference=False) self.assertEqual(20, count) self.assertEqual(20, len(fsList)) self.assertTrue(self.buildPath(["tree22", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_096(self): """ Attempt to add a directory with linkDepth=3, dereference=False. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=3, dereference=False) self.assertEqual(20, count) self.assertEqual(20, len(fsList)) self.assertTrue(self.buildPath(["tree22", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "link001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_097(self): """ Attempt to add a directory with linkDepth=0, dereference=True. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=0, dereference=True) self.assertEqual(12, count) self.assertEqual(12, len(fsList)) self.assertTrue(self.buildPath(["tree22", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_098(self): """ Attempt to add a directory with linkDepth=1, dereference=True. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=1, dereference=True) self.assertEqual(20, count) self.assertEqual(20, len(fsList)) self.assertTrue(self.buildPath(["tree22", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_099(self): """ Attempt to add a directory with linkDepth=2, dereference=True. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=2, dereference=True) self.assertEqual(32, count) self.assertEqual(32, len(fsList)) self.assertTrue(self.buildPath(["tree22", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir002", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir004", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir004", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir006", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir006", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_100(self): """ Attempt to add a directory with linkDepth=3, dereference=True. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) fsList = FilesystemList() count = fsList.addDirContents(path, linkDepth=3, dereference=True) self.assertEqual(35, count) self.assertEqual(35, len(fsList)) self.assertTrue(self.buildPath(["tree22", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir002", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir004", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir004", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir005", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir006", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir006", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir007", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree22", "dir008", "file001"]) in fsList) def testAddDirContents_101(self): """ Attempt to add a soft link; excludeFiles and dereference set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeFiles = True self.assertRaises(ValueError, fsList.addDirContents, path, True, True, 1, True) path = self.buildPath(["tree5", "dir002", "link001"]) # link to dir003 fsList = FilesystemList() fsList.excludeFiles = True count = fsList.addDirContents(path, True, True, 1, True) self.assertEqual(1, count) self.assertEqual([path], fsList) def testAddDirContents_102(self): """ Attempt to add a soft link; excludeDirs and dereference set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeDirs = True self.assertRaises(ValueError, fsList.addDirContents, path, True, True, 1, True) path = self.buildPath(["tree5", "dir002", "link001"]) # link to dir003 fsList = FilesystemList() fsList.excludeDirs = True count = fsList.addDirContents(path, True, True, 1, True) self.assertEqual(0, count) self.assertEqual([], fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_103(self): """ Attempt to add a soft link; excludeLinks and dereference set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeLinks = True self.assertRaises(ValueError, fsList.addDirContents, path, True, True, 1, True) path = self.buildPath(["tree5", "dir002", "link001"]) # link to dir003 fsList = FilesystemList() fsList.excludeLinks = True count = fsList.addDirContents(path, True, True, 1, True) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_104(self): """ Attempt to add a soft link; with excludePaths including the path, with dereference=True. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePaths = [path] self.assertRaises(ValueError, fsList.addDirContents, path, True, True, 1, True) path = self.buildPath(["tree5", "dir002", "link001"]) # link to dir003 fsList = FilesystemList() fsList.excludePaths = [path] count = fsList.addDirContents(path, True, True, 1, True) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_105(self): """ Attempt to add a soft link; with excludePatterns matching the path, with dereference=True. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, fsList.addDirContents, path, True, True, 1, True) path = self.buildPath(["tree5", "dir002", "link001"]) # link to dir003 fsList = FilesystemList() fsList.excludePatterns = [self.pathPattern(path)] count = fsList.addDirContents(path, True, True, 1, True) self.assertEqual(0, count) self.assertEqual([], fsList) def testAddDirContents_106(self): """ Attempt to add a link to a file, with dereference=True. """ self.extractTar("tree9") path = self.buildPath(["tree9", "dir002", "link003"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDirContents, path, True, True, 1, True) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_107(self): """ Attempt to add a link to a directory (which should add its contents), with dereference=True. """ self.extractTar("tree9") path = self.buildPath(["tree9", "link002"]) fsList = FilesystemList() count = fsList.addDirContents(path, True, True, 1, True) self.assertEqual(13, count) self.assertEqual(13, len(fsList)) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "dir001"]) in fsList) # duplicated self.assertTrue(self.buildPath(["tree9", "link002", "dir002"]) in fsList) # duplicated self.assertTrue(self.buildPath(["tree9", "link002", "file001"]) in fsList) # duplicated self.assertTrue(self.buildPath(["tree9", "link002", "file002"]) in fsList) # duplicated self.assertTrue(self.buildPath(["tree9", "link002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002", "link004"]) in fsList) def testAddDirContents_108(self): """ Attempt to add an invalid link (i.e. a link that points to something that doesn't exist), and dereference=True. """ self.extractTar("tree10") path = self.buildPath(["tree10", "link001"]) fsList = FilesystemList() self.assertRaises(ValueError, fsList.addDirContents, path, True, True, 1, True) def testAddDirContents_109(self): """ Attempt to add directory containing an invalid link (i.e. a link that points to something that doesn't exist), and dereference=True. """ self.extractTar("tree10") path = self.buildPath(["tree10"]) fsList = FilesystemList() count = fsList.addDirContents(path, True, True, 1, True) self.assertEqual(3, count) self.assertEqual(3, len(fsList)) self.assertTrue(self.buildPath(["tree10"]) in fsList) self.assertTrue(self.buildPath(["tree10", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree10", "dir002"]) in fsList) def testAddDirContents_110(self): """ Attempt to add a soft link; with excludeBasenamePatterns matching the path, and dereference=True. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "link001", ] self.assertRaises(ValueError, fsList.addDirContents, path, True, True, 1, True) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir fsList = FilesystemList() fsList.excludeBasenamePatterns = [ "link001", ] count = fsList.addDirContents(path, True, True, 1, True) self.assertEqual(0, count) self.assertEqual([], fsList) ##################### # Test removeFiles() ##################### def testRemoveFiles_001(self): """ Test with an empty list and a pattern of None. """ fsList = FilesystemList() count = fsList.removeFiles(pattern=None) self.assertEqual(0, count) def testRemoveFiles_002(self): """ Test with an empty list and a non-empty pattern. """ fsList = FilesystemList() count = fsList.removeFiles(pattern="pattern") self.assertEqual(0, count) self.assertRaises(ValueError, fsList.removeFiles, pattern="*.jpg") def testRemoveFiles_003(self): """ Test with a non-empty list (files only) and a pattern of None. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeFiles(pattern=None) self.assertEqual(7, count) self.assertEqual(1, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) def testRemoveFiles_004(self): """ Test with a non-empty list (directories only) and a pattern of None. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeFiles(pattern=None) self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveFiles_005(self): """ Test with a non-empty list (files and directories) and a pattern of None. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeFiles(pattern=None) self.assertEqual(44, count) self.assertEqual(37, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) def testRemoveFiles_006(self): """ Test with a non-empty list (files, directories and links) and a pattern of None. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeFiles(pattern=None) self.assertEqual(10, count) self.assertEqual(12, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveFiles_007(self): """ Test with a non-empty list (files and directories, some nonexistent) and a pattern of None. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeFiles(pattern=None) self.assertEqual(44, count) self.assertEqual(38, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveFiles_008(self): """ Test with a non-empty list (spaces in path names) and a pattern of None. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeFiles(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testRemoveFiles_009(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches none of the files. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeFiles(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveFiles_010(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches none of the files. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeFiles(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveFiles_011(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches none of the files. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeFiles(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveFiles_012(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches none of the files. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeFiles(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveFiles_013(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches none of the files. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeFiles(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveFiles_014(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches none of the files. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeFiles(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testRemoveFiles_015(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches some of the files. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeFiles(pattern=".*tree1.*file00[67]") self.assertEqual(2, count) self.assertEqual(6, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) def testRemoveFiles_016(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches some of the files. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeFiles(pattern=".*tree2.*") self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveFiles_017(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches some of the files. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeFiles(pattern=".*tree4.*dir006.*") self.assertEqual(10, count) self.assertEqual(71, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveFiles_018(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches some of the files. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeFiles(pattern=".*tree9.*dir002.*") self.assertEqual(4, count) self.assertEqual(18, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveFiles_019(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches some of the files. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeFiles(pattern=".*dir001.*file002.*") self.assertEqual(1, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveFiles_020(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches some of the files. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeFiles(pattern=".*with spaces.*") self.assertEqual(6, count) self.assertEqual(10, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) def testRemoveFiles_021(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches anything. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeFiles(pattern=".*") self.assertEqual(7, count) self.assertEqual(1, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) def testRemoveFiles_022(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches anything. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeFiles(pattern=".*") self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveFiles_023(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches anything. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeFiles(pattern=".*") self.assertEqual(44, count) self.assertEqual(37, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) def testRemoveFiles_024(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches all of the files. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeFiles(pattern=".*") self.assertEqual(10, count) self.assertEqual(12, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveFiles_025(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches all of the files. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeFiles(pattern=".*") self.assertEqual(44, count) self.assertEqual(38, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveFiles_026(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches all of the files. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeFiles(pattern=".*") self.assertEqual(11, count) self.assertEqual(5, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) #################### # Test removeDirs() #################### def testRemoveDirs_001(self): """ Test with an empty list and a pattern of None. """ fsList = FilesystemList() count = fsList.removeDirs(pattern=None) self.assertEqual(0, count) def testRemoveDirs_002(self): """ Test with an empty list and a non-empty pattern. """ fsList = FilesystemList() count = fsList.removeDirs(pattern="pattern") self.assertEqual(0, count) self.assertRaises(ValueError, fsList.removeDirs, pattern="*.jpg") def testRemoveDirs_003(self): """ Test with a non-empty list (files only) and a pattern of None. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeDirs(pattern=None) self.assertEqual(1, count) self.assertEqual(7, len(fsList)) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveDirs_004(self): """ Test with a non-empty list (directories only) and a pattern of None. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeDirs(pattern=None) self.assertEqual(11, count) self.assertEqual(0, len(fsList)) def testRemoveDirs_005(self): """ Test with a non-empty list (files and directories) and a pattern of None. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeDirs(pattern=None) self.assertEqual(37, count) self.assertEqual(44, len(fsList)) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveDirs_006(self): """ Test with a non-empty list (files, directories and links) and a pattern of None. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeDirs(pattern=None) self.assertEqual(12, count) self.assertEqual(10, len(fsList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) def testRemoveDirs_007(self): """ Test with a non-empty list (files and directories, some nonexistent) and a pattern of None. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeDirs(pattern=None) self.assertEqual(37, count) self.assertEqual(45, len(fsList)) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveDirs_008(self): """ Test with a non-empty list (spaces in path names) and a pattern of None. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeDirs(pattern=None) self.assertEqual(5, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testRemoveDirs_009(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches none of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeDirs(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveDirs_010(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches none of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeDirs(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveDirs_011(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches none of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeDirs(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveDirs_012(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches none of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeDirs(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveDirs_013(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches none of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeDirs(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveDirs_014(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches none of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeDirs(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testRemoveDirs_015(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches some of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeDirs(pattern=".*tree1.file00[67]") self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveDirs_016(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches some of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeDirs(pattern=".*dir0[012]0") self.assertEqual(1, count) self.assertEqual(10, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) def testRemoveDirs_017(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches some of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeDirs(pattern=".*dir001") self.assertEqual(9, count) self.assertEqual(72, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveDirs_018(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches some of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeDirs(pattern=".*tree9.*dir002.*") self.assertEqual(6, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveDirs_019(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches some of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeDirs(pattern=".*dir001") self.assertEqual(9, count) self.assertEqual(73, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveDirs_020(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches some of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeDirs(pattern=".*with spaces.*") self.assertEqual(1, count) self.assertEqual(15, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testRemoveDirs_021(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches all of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeDirs(pattern=".*") self.assertEqual(1, count) self.assertEqual(7, len(fsList)) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveDirs_022(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches all of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeDirs(pattern=".*") self.assertEqual(11, count) self.assertEqual(0, len(fsList)) def testRemoveDirs_023(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches all of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeDirs(pattern=".*") self.assertEqual(37, count) self.assertEqual(44, len(fsList)) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveDirs_024(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches all of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeDirs(pattern=".*") self.assertEqual(12, count) self.assertEqual(10, len(fsList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) def testRemoveDirs_025(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches all of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeDirs(pattern=".*") self.assertEqual(37, count) self.assertEqual(45, len(fsList)) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveDirs_026(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches all of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeDirs(pattern=".*") self.assertEqual(5, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) ##################### # Test removeLinks() ##################### def testRemoveLinks_001(self): """ Test with an empty list and a pattern of None. """ fsList = FilesystemList() count = fsList.removeLinks(pattern=None) self.assertEqual(0, count) def testRemoveLinks_002(self): """ Test with an empty list and a non-empty pattern. """ fsList = FilesystemList() count = fsList.removeLinks(pattern="pattern") self.assertEqual(0, count) self.assertRaises(ValueError, fsList.removeLinks, pattern="*.jpg") def testRemoveLinks_003(self): """ Test with a non-empty list (files only) and a pattern of None. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeLinks(pattern=None) self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveLinks_004(self): """ Test with a non-empty list (directories only) and a pattern of None. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeLinks(pattern=None) self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveLinks_005(self): """ Test with a non-empty list (files and directories) and a pattern of None. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeLinks(pattern=None) self.assertEqual(0, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveLinks_006(self): """ Test with a non-empty list (files, directories and links) and a pattern of None. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeLinks(pattern=None) self.assertEqual(9, count) self.assertEqual(13, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) def testRemoveLinks_007(self): """ Test with a non-empty list (files and directories, some nonexistent) and a pattern of None. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeLinks(pattern=None) self.assertEqual(0, count) self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveLinks_008(self): """ Test with a non-empty list (spaces in path names) and a pattern of None. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeLinks(pattern=None) self.assertEqual(6, count) self.assertEqual(10, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) def testRemoveLinks_009(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches none of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeLinks(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveLinks_010(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches none of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeLinks(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveLinks_011(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches none of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeLinks(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveLinks_012(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches none of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeLinks(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveLinks_013(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches none of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeLinks(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveLinks_014(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches none of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeLinks(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testRemoveLinks_015(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches some of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeLinks(pattern=".*tree1.*file007") self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveLinks_016(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches some of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeLinks(pattern=".*tree2.*") self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveLinks_017(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches some of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeLinks(pattern=".*tree4.*dir006.*") self.assertEqual(0, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveLinks_018(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches some of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeLinks(pattern=".*tree9.*dir002.*") self.assertEqual(4, count) self.assertEqual(18, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveLinks_019(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches some of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeLinks(pattern=".*tree4.*dir006.*") self.assertEqual(0, count) self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveLinks_020(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches some of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeLinks(pattern=".*with spaces.*") self.assertEqual(3, count) self.assertEqual(13, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) def testRemoveLinks_021(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches all of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeLinks(pattern=".*") self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveLinks_022(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches all of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeLinks(pattern=".*") self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveLinks_023(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches all of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeLinks(pattern=".*") self.assertEqual(0, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveLinks_024(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches all of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeLinks(pattern=".*") self.assertEqual(9, count) self.assertEqual(13, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) def testRemoveLinks_025(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches all of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeLinks(pattern=".*") self.assertEqual(0, count) self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveLinks_026(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches all of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeLinks(pattern=".*") self.assertEqual(6, count) self.assertEqual(10, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) ##################### # Test removeMatch() ##################### def testRemoveMatch_001(self): """ Test with an empty list and a pattern of None. """ fsList = FilesystemList() self.assertRaises(TypeError, fsList.removeMatch, pattern=None) def testRemoveMatch_002(self): """ Test with an empty list and a non-empty pattern. """ fsList = FilesystemList() count = fsList.removeMatch(pattern="pattern") self.assertEqual(0, count) self.assertRaises(ValueError, fsList.removeMatch, pattern="*.jpg") def testRemoveMatch_003(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches none of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeMatch(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveMatch_004(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches none of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeMatch(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveMatch_005(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches none of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeMatch(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveMatch_006(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches none of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeMatch(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveMatch_007(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches none of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeMatch(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveMatch_008(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches none of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeMatch(pattern=NOMATCH_PATTERN) self.assertEqual(0, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) def testRemoveMatch_009(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches some of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeMatch(pattern=".*file00[135].*") self.assertEqual(3, count) self.assertEqual(5, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveMatch_010(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches some of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeMatch(pattern=".*dir00[2468].*") self.assertEqual(4, count) self.assertEqual(7, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveMatch_011(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches some of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeMatch(pattern=".*tree4.*dir006") self.assertEqual(18, count) self.assertEqual(63, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveMatch_012(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches some of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeMatch(pattern=".*file001.*") self.assertEqual(3, count) self.assertEqual(19, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveMatch_013(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches some of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeMatch(pattern=".*dir00[46].*") self.assertEqual(25, count) self.assertEqual(57, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveMatch_014(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches some of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeMatch(pattern=".*with spaces.*") self.assertEqual(7, count) self.assertEqual(9, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) def testRemoveMatch_015(self): """ Test with a non-empty list (files only) and a non-empty pattern that matches all of them. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeMatch(pattern=".*") self.assertEqual(8, count) self.assertEqual(0, len(fsList)) def testRemoveMatch_016(self): """ Test with a non-empty list (directories only) and a non-empty pattern that matches all of them. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeMatch(pattern=".*") self.assertEqual(11, count) self.assertEqual(0, len(fsList)) def testRemoveMatch_017(self): """ Test with a non-empty list (files and directories) and a non-empty pattern that matches all of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeMatch(pattern=".*") self.assertEqual(81, count) self.assertEqual(0, len(fsList)) def testRemoveMatch_019(self): """ Test with a non-empty list (files, directories and links) and a non-empty pattern that matches all of them. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeMatch(pattern=".*") self.assertEqual(22, count) self.assertEqual(0, len(fsList)) def testRemoveMatch_020(self): """ Test with a non-empty list (files and directories, some nonexistent) and a non-empty pattern that matches all of them. """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) fsList.append(self.buildPath(["tree4", INVALID_FILE])) # file won't exist on disk self.assertEqual(82, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeMatch(pattern=".*") self.assertEqual(82, count) self.assertEqual(0, len(fsList)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveMatch_021(self): """ Test with a non-empty list (spaces in path names) and a non-empty pattern that matches all of them. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeMatch(pattern=".*") self.assertEqual(16, count) self.assertEqual(0, len(fsList)) ####################### # Test removeInvalid() ####################### def testRemoveInvalid_001(self): """ Test with an empty list. """ fsList = FilesystemList() count = fsList.removeInvalid() self.assertEqual(0, count) def testRemoveInvalid_002(self): """ Test with a non-empty list containing only invalid entries (some with spaces). """ self.extractTar("tree9") fsList = FilesystemList() fsList.append(self.buildPath(["tree9", "%s-1" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-2" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-3" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-4" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", " %s 5 " % INVALID_FILE])) # file won't exist on disk self.assertEqual(5, len(fsList)) self.assertTrue(self.buildPath(["tree9", "%s-1" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-2" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-3" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-4" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", " %s 5 " % INVALID_FILE]) in fsList) count = fsList.removeInvalid() self.assertEqual(5, count) self.assertEqual(0, len(fsList)) def testRemoveInvalid_003(self): """ Test with a non-empty list containing only valid entries (files only). """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) count = fsList.removeInvalid() self.assertEqual(0, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testRemoveInvalid_004(self): """ Test with a non-empty list containing only valid entries (directories only). """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) count = fsList.removeInvalid() self.assertEqual(0, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testRemoveInvalid_005(self): """ Test with a non-empty list containing only valid entries (files and directories). """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) count = fsList.removeInvalid() self.assertEqual(0, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testRemoveInvalid_006(self): """ Test with a non-empty list containing only valid entries (files, directories and links). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeInvalid() self.assertEqual(0, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testRemoveInvalid_007(self): """ Test with a non-empty list containing valid and invalid entries (files, directories and links). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) fsList.append(self.buildPath(["tree9", "%s-1" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-2" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-3" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-4" % INVALID_FILE])) # file won't exist on disk self.assertEqual(26, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-1" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-2" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-3" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-4" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) count = fsList.removeInvalid() self.assertEqual(4, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveInvalid_008(self): """ Test with a non-empty list containing only valid entries (files, directories and links, some with spaces). """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) count = fsList.removeInvalid() self.assertEqual(0, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) ################### # Test normalize() ################### def testNormalize_001(self): """ Test with an empty list. """ fsList = FilesystemList() self.assertEqual(0, len(fsList)) fsList.normalize() self.assertEqual(0, len(fsList)) def testNormalize_002(self): """ Test with a list containing one entry. """ fsList = FilesystemList() fsList.append("one") self.assertEqual(1, len(fsList)) fsList.normalize() self.assertEqual(1, len(fsList)) self.assertTrue("one" in fsList) def testNormalize_003(self): """ Test with a list containing two entries, no duplicates. """ fsList = FilesystemList() fsList.append("one") fsList.append("two") self.assertEqual(2, len(fsList)) fsList.normalize() self.assertEqual(2, len(fsList)) self.assertTrue("one" in fsList) self.assertTrue("two" in fsList) def testNormalize_004(self): """ Test with a list containing two entries, with duplicates. """ fsList = FilesystemList() fsList.append("one") fsList.append("one") self.assertEqual(2, len(fsList)) fsList.normalize() self.assertEqual(1, len(fsList)) self.assertTrue("one" in fsList) def testNormalize_005(self): """ Test with a list containing many entries, no duplicates. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) fsList.normalize() self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testNormalize_006(self): """ Test with a list containing many entries, with duplicates. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(44, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) fsList.normalize() self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) ################ # Test verify() ################ def testVerify_001(self): """ Test with an empty list. """ fsList = FilesystemList() ok = fsList.verify() self.assertEqual(True, ok) def testVerify_002(self): """ Test with a non-empty list containing only invalid entries. """ self.extractTar("tree9") fsList = FilesystemList() fsList.append(self.buildPath(["tree9", "%s-1" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-2" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-3" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-4" % INVALID_FILE])) # file won't exist on disk self.assertEqual(4, len(fsList)) self.assertTrue(self.buildPath(["tree9", "%s-1" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-2" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-3" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-4" % INVALID_FILE]) in fsList) ok = fsList.verify() self.assertEqual(False, ok) self.assertEqual(4, len(fsList)) self.assertTrue(self.buildPath(["tree9", "%s-1" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-2" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-3" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-4" % INVALID_FILE]) in fsList) def testVerify_003(self): """ Test with a non-empty list containing only valid entries (files only). """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) ok = fsList.verify() self.assertEqual(True, ok) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testVerify_004(self): """ Test with a non-empty list containing only valid entries (directories only). """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) ok = fsList.verify() self.assertEqual(True, ok) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) def testVerify_005(self): """ Test with a non-empty list containing only valid entries (files and directories). """ self.extractTar("tree4") path = self.buildPath(["tree4"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(81, count) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) ok = fsList.verify() self.assertEqual(True, ok) self.assertEqual(81, len(fsList)) self.assertTrue(self.buildPath(["tree4"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir001", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir002", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir003", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir004", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir005", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file007"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file008"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file009"]) in fsList) self.assertTrue(self.buildPath(["tree4", "dir006", "file010"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree4", "file007"]) in fsList) def testVerify_006(self): """ Test with a non-empty list containing only valid entries (files, directories and links). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) ok = fsList.verify() self.assertEqual(True, ok) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testVerify_007(self): """ Test with a non-empty list containing valid and invalid entries (files, directories and links). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) fsList.append(self.buildPath(["tree9", "%s-1" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-2" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-3" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree9", "%s-4" % INVALID_FILE])) # file won't exist on disk self.assertEqual(26, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-1" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-2" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-3" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-4" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) ok = fsList.verify() self.assertEqual(False, ok) self.assertEqual(26, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-1" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-2" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-3" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "%s-4" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testVerify_008(self): """ Test with a non-empty list containing valid and invalid entries (some containing spaces). """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) fsList.append(self.buildPath(["tree11", "dir with spaces", "%s-1" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree11", "dir with spaces", "%s-2" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree11", "dir with spaces", "%s-3" % INVALID_FILE])) # file won't exist on disk fsList.append(self.buildPath(["tree11", "dir with spaces", "%s-4" % INVALID_FILE])) # file won't exist on disk self.assertEqual(20, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "%s-1" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "%s-2" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "%s-3" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "%s-4" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) ok = fsList.verify() self.assertEqual(False, ok) self.assertEqual(20, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "%s-1" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "%s-2" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "%s-3" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "%s-4" % INVALID_FILE]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) ########################### # TestBackupFileList class ########################### class TestBackupFileList(unittest.TestCase): """Tests for the BackupFileList class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) def tarPath(self, components): """Builds a complete search path from a list of components, compatible with Python tar output.""" result = self.buildPath(components) if result[0:1] == os.path.sep: return result[1:] return result def buildRandomPath(self, maxlength, extension): """Builds a complete, randomly-named search path.""" maxlength -= len(self.tmpdir) maxlength -= len(extension) components = [ self.tmpdir, randomFilename(maxlength, suffix=extension), ] return buildPath(components) ################ # Test addDir() ################ def testAddDir_001(self): """ Test that function is overridden, no exclusions. Since this function calls the superclass by definition, we can skimp a bit on validation and only ensure that it seems to be overridden properly. """ self.extractTar("tree5") backupList = BackupFileList() dirPath = self.buildPath(["tree5", "dir001"]) count = backupList.addDir(dirPath) self.assertEqual(0, count) self.assertEqual(0, len(backupList)) if platformSupportsLinks(): dirPath = self.buildPath(["tree5", "dir002", "link001"]) count = backupList.addDir(dirPath) self.assertEqual(1, count) self.assertEqual([dirPath], backupList) def testAddDir_002(self): """ Test that function is overridden, excludeFiles set. Since this function calls the superclass by definition, we can skimp a bit on validation and only ensure that it seems to be overridden properly. """ self.extractTar("tree5") backupList = BackupFileList() backupList.excludeFiles = True dirPath = self.buildPath(["tree5", "dir001"]) count = backupList.addDir(dirPath) self.assertEqual(0, count) self.assertEqual(0, len(backupList)) if platformSupportsLinks(): dirPath = self.buildPath(["tree5", "dir002", "link001"]) count = backupList.addDir(dirPath) self.assertEqual(1, count) self.assertEqual([dirPath], backupList) def testAddDir_003(self): """ Test that function is overridden, excludeDirs set. Since this function calls the superclass by definition, we can skimp a bit on validation and only ensure that it seems to be overridden properly. """ self.extractTar("tree5") backupList = BackupFileList() backupList.excludeDirs = True dirPath = self.buildPath(["tree5", "dir001"]) count = backupList.addDir(dirPath) self.assertEqual(0, count) self.assertEqual(0, len(backupList)) dirPath = self.buildPath(["tree5", "dir002", "link001"]) count = backupList.addDir(dirPath) self.assertEqual(0, count) self.assertEqual(0, len(backupList)) def testAddDir_004(self): """ Test that function is overridden, excludeLinks set. Since this function calls the superclass by definition, we can skimp a bit on validation and only ensure that it seems to be overridden properly. """ self.extractTar("tree5") backupList = BackupFileList() backupList.excludeLinks = True dirPath = self.buildPath(["tree5", "dir001"]) count = backupList.addDir(dirPath) self.assertEqual(0, count) self.assertEqual(0, len(backupList)) dirPath = self.buildPath(["tree5", "dir002", "link001"]) count = backupList.addDir(dirPath) self.assertEqual(0, count) self.assertEqual(0, len(backupList)) def testAddDir_005(self): """ Test that function is overridden, excludePaths set. Since this function calls the superclass by definition, we can skimp a bit on validation and only ensure that it seems to be overridden properly. """ self.extractTar("tree5") backupList = BackupFileList() backupList.excludePaths = [NOMATCH_PATH] dirPath = self.buildPath(["tree5", "dir001"]) count = backupList.addDir(dirPath) self.assertEqual(0, count) self.assertEqual(0, len(backupList)) if platformSupportsLinks(): dirPath = self.buildPath(["tree5", "dir002", "link001"]) count = backupList.addDir(dirPath) self.assertEqual(1, count) self.assertEqual([dirPath], backupList) def testAddDir_006(self): """ Test that function is overridden, excludePatterns set. Since this function calls the superclass by definition, we can skimp a bit on validation and only ensure that it seems to be overridden properly. """ self.extractTar("tree5") backupList = BackupFileList() backupList.excludePatterns = [NOMATCH_PATH] dirPath = self.buildPath(["tree5", "dir001"]) count = backupList.addDir(dirPath) self.assertEqual(0, count) self.assertEqual(0, len(backupList)) if platformSupportsLinks(): dirPath = self.buildPath(["tree5", "dir002", "link001"]) count = backupList.addDir(dirPath) self.assertEqual(1, count) self.assertEqual([dirPath], backupList) ################### # Test totalSize() ################### def testTotalSize_001(self): """ Test on an empty list. """ backupList = BackupFileList() size = backupList.totalSize() self.assertEqual(0, size) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testTotalSize_002(self): """ Test on a non-empty list containing only valid entries. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) size = backupList.totalSize() self.assertEqual(1116, size) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testTotalSize_004(self): """ Test on a non-empty list (some containing spaces). """ self.extractTar("tree11") path = self.buildPath(["tree11"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(13, count) self.assertEqual(13, len(backupList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in backupList) size = backupList.totalSize() self.assertEqual(1085, size) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testTotalSize_005(self): """ Test on a non-empty list containing a directory (which shouldn't be possible). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", "dir001"])) # back-door around addDir() self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) size = backupList.totalSize() self.assertEqual(1116, size) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testTotalSize_006(self): """ Test on a non-empty list containing a non-existent file. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", INVALID_FILE])) # file won't exist on disk self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", INVALID_FILE]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) size = backupList.totalSize() self.assertEqual(1116, size) ######################### # Test generateSizeMap() ######################### def testGenerateSizeMap_001(self): """ Test on an empty list. """ backupList = BackupFileList() sizeMap = backupList.generateSizeMap() self.assertEqual(0, len(sizeMap)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateSizeMap_002(self): """ Test on a non-empty list containing only valid entries. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) sizeMap = backupList.generateSizeMap() self.assertEqual(15, len(sizeMap)) self.assertEqual(243, sizeMap[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual(268, sizeMap[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link003"])]) self.assertEqual(134, sizeMap[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual(74, sizeMap[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link003"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link004"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree9", "file001"])]) self.assertEqual(242, sizeMap[self.buildPath(["tree9", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "link002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateSizeMap_004(self): """ Test on a non-empty list (some containing spaces). """ self.extractTar("tree11") path = self.buildPath(["tree11"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(13, count) self.assertEqual(13, len(backupList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in backupList) sizeMap = backupList.generateSizeMap() self.assertEqual(13, len(sizeMap)) self.assertEqual(155, sizeMap[self.buildPath(["tree11", "file001"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree11", "file with spaces"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree11", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree11", "link002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree11", "link003"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree11", "link with spaces"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree11", "dir002", "file001"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree11", "dir002", "file002"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree11", "dir002", "file003"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree11", "dir with spaces", "file001"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree11", "dir with spaces", "file with spaces"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree11", "dir with spaces", "link002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree11", "dir with spaces", "link with spaces"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateSizeMap_005(self): """ Test on a non-empty list containing a directory (which shouldn't be possible). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", "dir001"])) # back-door around addDir() self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) sizeMap = backupList.generateSizeMap() self.assertEqual(15, len(sizeMap)) self.assertEqual(243, sizeMap[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual(268, sizeMap[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link003"])]) self.assertEqual(134, sizeMap[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual(74, sizeMap[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link003"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link004"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree9", "file001"])]) self.assertEqual(242, sizeMap[self.buildPath(["tree9", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "link002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateSizeMap_006(self): """ Test on a non-empty list containing a non-existent file. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", INVALID_FILE])) # file won't exist on disk self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", INVALID_FILE]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) sizeMap = backupList.generateSizeMap() self.assertEqual(15, len(sizeMap)) self.assertEqual(243, sizeMap[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual(268, sizeMap[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir001", "link003"])]) self.assertEqual(134, sizeMap[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual(74, sizeMap[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link003"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "dir002", "link004"])]) self.assertEqual(155, sizeMap[self.buildPath(["tree9", "file001"])]) self.assertEqual(242, sizeMap[self.buildPath(["tree9", "file002"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "link001"])]) self.assertEqual(0, sizeMap[self.buildPath(["tree9", "link002"])]) ########################### # Test generateDigestMap() ########################### def testGenerateDigestMap_001(self): """ Test on an empty list. """ backupList = BackupFileList() digestMap = backupList.generateDigestMap() self.assertEqual(0, len(digestMap)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateDigestMap_002(self): """ Test on a non-empty list containing only valid entries. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) digestMap = backupList.generateDigestMap() self.assertEqual(6, len(digestMap)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", digestMap[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", digestMap[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", digestMap[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", digestMap[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", digestMap[self.buildPath(["tree9", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateDigestMap_003(self): """ Test on a non-empty list containing only valid entries (some containing spaces). """ self.extractTar("tree11") path = self.buildPath(["tree11"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(13, count) self.assertEqual(13, len(backupList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in backupList) digestMap = backupList.generateDigestMap() self.assertEqual(7, len(digestMap)) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree11", "file001"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree11", "file with spaces"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree11", "dir002", "file001"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree11", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree11", "dir002", "file003"])]) self.assertEqual( "3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree11", "dir with spaces", "file001"])] ) self.assertEqual( "3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree11", "dir with spaces", "file with spaces"])], ) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateDigestMap_004(self): """ Test on a non-empty list containing a directory (which shouldn't be possible). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", "dir001"])) # back-door around addDir() self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) digestMap = backupList.generateDigestMap() self.assertEqual(6, len(digestMap)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", digestMap[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", digestMap[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", digestMap[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", digestMap[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", digestMap[self.buildPath(["tree9", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateDigestMap_005(self): """ Test on a non-empty list containing a non-existent file. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", INVALID_FILE])) # file won't exist on disk self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", INVALID_FILE]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) digestMap = backupList.generateDigestMap() self.assertEqual(6, len(digestMap)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", digestMap[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", digestMap[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", digestMap[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", digestMap[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", digestMap[self.buildPath(["tree9", "file002"])]) def testGenerateDigestMap_006(self): """ Test on an empty list, passing stripPrefix not None. """ backupList = BackupFileList() prefix = "whatever" digestMap = backupList.generateDigestMap(stripPrefix=prefix) self.assertEqual(0, len(digestMap)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateDigestMap_007(self): """ Test on a non-empty list containing only valid entries, passing stripPrefix not None. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) prefix = normalizeDir(self.buildPath(["tree9"])) digestMap = backupList.generateDigestMap(stripPrefix=prefix) self.assertEqual(6, len(digestMap)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", digestMap[buildPath(["/", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", digestMap[buildPath(["/", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", digestMap[buildPath(["/", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", digestMap[buildPath(["/", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", digestMap[buildPath(["/", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateDigestMap_008(self): """ Test on a non-empty list containing only valid entries (some containing spaces), passing stripPrefix not None. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(13, count) self.assertEqual(13, len(backupList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in backupList) prefix = normalizeDir(self.buildPath(["tree11"])) digestMap = backupList.generateDigestMap(stripPrefix=prefix) self.assertEqual(7, len(digestMap)) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "file001"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "file with spaces"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "dir002", "file001"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "dir002", "file003"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "dir with spaces", "file001"])]) self.assertEqual( "3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "dir with spaces", "file with spaces"])] ) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateDigestMap_009(self): """ Test on a non-empty list containing a directory (which shouldn't be possible), passing stripPrefix not None. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", "dir001"])) # back-door around addDir() self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) prefix = normalizeDir(self.buildPath(["tree9"])) digestMap = backupList.generateDigestMap(stripPrefix=prefix) self.assertEqual(6, len(digestMap)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", digestMap[buildPath(["/", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", digestMap[buildPath(["/", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", digestMap[buildPath(["/", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", digestMap[buildPath(["/", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", digestMap[buildPath(["/", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateDigestMap_010(self): """ Test on a non-empty list containing a non-existent file, passing stripPrefix not None. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", INVALID_FILE])) # file won't exist on disk self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", INVALID_FILE]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) prefix = normalizeDir(self.buildPath(["tree9"])) digestMap = backupList.generateDigestMap(stripPrefix=prefix) self.assertEqual(6, len(digestMap)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", digestMap[buildPath(["/", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", digestMap[buildPath(["/", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", digestMap[buildPath(["/", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", digestMap[buildPath(["/", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", digestMap[buildPath(["/", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", digestMap[buildPath(["/", "file002"])]) ######################## # Test generateFitted() ######################## def testGenerateFitted_001(self): """ Test on an empty list. """ backupList = BackupFileList() fittedList = backupList.generateFitted(2000) self.assertEqual(0, len(fittedList)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateFitted_002(self): """ Test on a non-empty list containing only valid entries, all of which fit. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) fittedList = backupList.generateFitted(2000) self.assertEqual(15, len(fittedList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fittedList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateFitted_003(self): """ Test on a non-empty list containing only valid entries (some containing spaces), all of which fit. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(13, count) self.assertEqual(13, len(backupList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in backupList) fittedList = backupList.generateFitted(2000) self.assertEqual(13, len(fittedList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fittedList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateFitted_004(self): """ Test on a non-empty list containing only valid entries, some of which fit. We can get some strange behavior on Windows, which hits the "links not supported" case. The file tree9/dir002/file002 is 74 bytes, and is supposed to be the only file included because links are not recognized. However, link004 points at file002, and apparently Windows (sometimes?) sees link004 as a real file with a size of 74 bytes. Since only one of the two fits in the fitted list, we just check for one or the other. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) fittedList = backupList.generateFitted(80) self.assertEqual(10, len(fittedList)) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fittedList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateFitted_005(self): """ Test on a non-empty list containing only valid entries, none of which fit. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) fittedList = backupList.generateFitted(0) self.assertEqual(0, len(fittedList)) fittedList = backupList.generateFitted(50) self.assertEqual(9, len(fittedList)) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fittedList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateFitted_006(self): """ Test on a non-empty list containing a directory (which shouldn't be possible). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", "dir001"])) # back-door around addDir() self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) fittedList = backupList.generateFitted(2000) self.assertEqual(15, len(fittedList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fittedList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateFitted_007(self): """ Test on a non-empty list containing a non-existent file. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", INVALID_FILE])) # file won't exist on disk self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", INVALID_FILE]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) fittedList = backupList.generateFitted(2000) self.assertEqual(15, len(fittedList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fittedList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fittedList) ###################### # Test generateSpan() ###################### def testGenerateSpan_001(self): """ Test on an empty list. """ backupList = BackupFileList() spanSet = backupList.generateSpan(2000) self.assertEqual(0, len(spanSet)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateSpan_002(self): """ Test a set of files that all fit in one span item. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) spanSet = backupList.generateSpan(2000) self.assertEqual(1, len(spanSet)) spanItem = spanSet[0] self.assertEqual(15, len(spanItem.fileList)) self.assertEqual(1116, spanItem.size) self.assertEqual(2000, spanItem.capacity) self.assertEqual((1116.0 / 2000.0) * 100.0, spanItem.utilization) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "file001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "file002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "link002"]) in spanItem.fileList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateSpan_003(self): """ Test a set of files that all fit in two span items. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) spanSet = backupList.generateSpan(760, "best_fit") self.assertEqual(2, len(spanSet)) spanItem = spanSet[0] self.assertEqual(12, len(spanItem.fileList)) self.assertEqual(753, spanItem.size) self.assertEqual(760, spanItem.capacity) self.assertEqual((753.0 / 760.0) * 100.0, spanItem.utilization) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "file002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "link002"]) in spanItem.fileList) spanItem = spanSet[1] self.assertEqual(3, len(spanItem.fileList)) self.assertEqual(363, spanItem.size) self.assertEqual(760, spanItem.capacity) self.assertEqual((363.0 / 760.0) * 100.0, spanItem.utilization) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "file001"]) in spanItem.fileList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateSpan_004(self): """ Test a set of files that all fit in three span items. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) spanSet = backupList.generateSpan(515, "best_fit") self.assertEqual(3, len(spanSet)) spanItem = spanSet[0] self.assertEqual(11, len(spanItem.fileList)) self.assertEqual(511, spanItem.size) self.assertEqual(515, spanItem.capacity) self.assertEqual((511.0 / 515.0) * 100.0, spanItem.utilization) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "link001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "link002"]) in spanItem.fileList) spanItem = spanSet[1] self.assertEqual(3, len(spanItem.fileList)) self.assertEqual(471, spanItem.size) self.assertEqual(515, spanItem.capacity) self.assertEqual((471.0 / 515.0) * 100.0, spanItem.utilization) self.assertTrue(self.buildPath(["tree9", "file002"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "file001"]) in spanItem.fileList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in spanItem.fileList) spanItem = spanSet[2] self.assertEqual(1, len(spanItem.fileList)) self.assertEqual(134, spanItem.size) self.assertEqual(515, spanItem.capacity) self.assertEqual((134.0 / 515.0) * 100.0, spanItem.utilization) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in spanItem.fileList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateSpan_005(self): """ Test a set of files where one of the files does not fit in the capacity. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) self.assertRaises(ValueError, backupList.generateSpan, 250, "best_fit") ######################### # Test generateTarfile() ######################### def testGenerateTarfile_001(self): """ Test on an empty list. """ backupList = BackupFileList() tarPath = self.buildPath(["file.tar"]) self.assertRaises(ValueError, backupList.generateTarfile, tarPath) self.assertTrue(not os.path.exists(tarPath)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_002(self): """ Test on a non-empty list containing a directory (which shouldn't be possible). """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", "dir001"])) # back-door around addDir() self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildPath(["file.tar"]) backupList.generateTarfile(tarPath) self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(16, len(tarList)) self.assertTrue( self.tarPath(["tree9", "dir001/"]) in tarList or self.tarPath(["tree9", "dir001//"]) in tarList # Grr... Python 2.5 behavior differs or self.tarPath(["tree9", "dir001"]) in tarList ) # Grr... Python 2.6 behavior differs self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_003(self): """ Test on a non-empty list containing a non-existent file, ignore=False. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", INVALID_FILE])) # file won't exist on disk self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", INVALID_FILE]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildPath(["file.tar"]) self.assertRaises(tarfile.TarError, backupList.generateTarfile, tarPath, ignore=False) self.assertTrue(not os.path.exists(tarPath)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_004(self): """ Test on a non-empty list containing a non-existent file, ignore=True. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) backupList.append(self.buildPath(["tree9", INVALID_FILE])) # file won't exist on disk self.assertEqual(16, len(backupList)) self.assertTrue(self.buildPath(["tree9", INVALID_FILE]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildPath(["file.tar"]) backupList.generateTarfile(tarPath, ignore=True) self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(15, len(tarList)) self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_005(self): """ Test on a non-empty list containing only valid entries, with an invalid mode. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildPath(["file.tar"]) self.assertRaises(ValueError, backupList.generateTarfile, tarPath, mode="bogus") self.assertTrue(not os.path.exists(tarPath)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_006(self): """ Test on a non-empty list containing only valid entries, default mode. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildPath(["file.tar"]) backupList.generateTarfile(tarPath) self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(15, len(tarList)) self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_007(self): """ Test on a non-empty list (some containing spaces), default mode. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(13, count) self.assertEqual(13, len(backupList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in backupList) tarPath = self.buildPath(["file.tar"]) backupList.generateTarfile(tarPath) self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(13, len(tarList)) self.assertTrue(self.tarPath(["tree11", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree11", "file with spaces"]) in tarList) self.assertTrue(self.tarPath(["tree11", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree11", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree11", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree11", "link with spaces"]) in tarList) self.assertTrue(self.tarPath(["tree11", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree11", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree11", "dir002", "file003"]) in tarList) self.assertTrue(self.tarPath(["tree11", "dir with spaces", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree11", "dir with spaces", "file with spaces"]) in tarList) self.assertTrue(self.tarPath(["tree11", "dir with spaces", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree11", "dir with spaces", "link with spaces"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_008(self): """ Test on a non-empty list containing only valid entries, 'tar' mode. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildPath(["file.tar"]) backupList.generateTarfile(tarPath) self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(15, len(tarList)) self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_009(self): """ Test on a non-empty list containing only valid entries, 'targz' mode. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildPath(["file.tar.gz"]) backupList.generateTarfile(tarPath, mode="targz") self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(15, len(tarList)) self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_010(self): """ Test on a non-empty list containing only valid entries, 'tarbz2' mode. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildPath(["file.tar.bz2"]) backupList.generateTarfile(tarPath, mode="tarbz2") self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(15, len(tarList)) self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_011(self): """ Test on a non-empty list containing only valid entries, 'tar' mode, long target name. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildRandomPath(255, ".tar") backupList.generateTarfile(tarPath, mode="tar") self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(15, len(tarList)) self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_012(self): """ Test on a non-empty list containing only valid entries, 'targz' mode, long target name. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildRandomPath(255, ".tar") backupList.generateTarfile(tarPath, mode="targz") self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(15, len(tarList)) self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testGenerateTarfile_013(self): """ Test on a non-empty list containing only valid entries, 'tarbz2' mode, long target name. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) tarPath = self.buildRandomPath(255, ".tar") backupList.generateTarfile(tarPath, mode="tarbz2") self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(15, len(tarList)) self.assertTrue(self.tarPath(["tree9", "dir001", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir001", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link003"]) in tarList) self.assertTrue(self.tarPath(["tree9", "dir002", "link004"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "file002"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link001"]) in tarList) self.assertTrue(self.tarPath(["tree9", "link002"]) in tarList) def testGenerateTarfile_014(self): """ Test behavior of the flat flag. """ self.extractTar("tree11") backupList = BackupFileList() path = self.buildPath(["tree11", "dir with spaces", "file with spaces"]) backupList.addFile(path) path = self.buildPath(["tree11", "dir with spaces", "file001"]) backupList.addFile(path) path = self.buildPath(["tree11", "dir002", "file002"]) backupList.addFile(path) path = self.buildPath(["tree11", "dir002", "file003"]) backupList.addFile(path) self.assertEqual(4, len(backupList)) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in backupList) tarPath = self.buildPath(["file.tar"]) backupList.generateTarfile(tarPath, flat=True) self.assertTrue(tarfile.is_tarfile(tarPath)) with tarfile.open(tarPath) as tarFile: tarList = tarFile.getnames() self.assertEqual(4, len(tarList)) self.assertTrue("file with spaces" in tarList) self.assertTrue("file001" in tarList) self.assertTrue("file002" in tarList) self.assertTrue("file003" in tarList) ######################### # Test removeUnchanged() ######################### def testRemoveUnchanged_001(self): """ Test on an empty list with an empty digest map. """ digestMap = {} backupList = BackupFileList() self.assertEqual(0, len(backupList)) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(0, len(backupList)) def testRemoveUnchanged_002(self): """ Test on an empty list with an non-empty digest map. """ digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531c7e897cd3df90ed76355de7e21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir002", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir002", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file002"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() self.assertEqual(0, len(backupList)) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(0, len(backupList)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_003(self): """ Test on an non-empty list with an empty digest map. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = {} backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_004(self): """ Test with a digest map containing only entries that are not in the list. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir003", "file001"]): "4ff529531c7e897cd3df90ed76355de7e21e77ee", self.buildPath(["tree9", "dir003", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir004", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir004", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file003"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file004"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_005(self): """ Test with a digest map containing only entries that are in the list, with non-matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531c7e8AAAAAAAAAAAAAAAAAA7e21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecAAAAAAAAAAAAAAAAAA95d1d6cba", self.buildPath(["tree9", "dir002", "file001"]): "2f68cdda26b64AAAAAAAAAAAAAAAAAA5b8786c4b", self.buildPath(["tree9", "dir002", "file002"]): "0cc03b3014d1cAAAAAAAAAAAAAAAAAA5d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237aAAAAAAAAAAAAAAAAAA555973847", self.buildPath(["tree9", "file002"]): "fae89085ee97bAAAAAAAAAAAAAAAAAAbb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_006(self): """ Test with a digest map containing only entries that are in the list, with matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531c7e897cd3df90ed76355de7e21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir002", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir002", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file002"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(6, count) self.assertEqual(9, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_007(self): """ Test with a digest map containing both entries that are and are not in the list, with non-matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531cCCCCCCCCCCCCCCCCCCCCCCCCCe77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a2CCCCCCCCCCCCCCCCCCCCCCCCCd6cba", self.buildPath(["tree9", "dir003", "file001"]): "2f68cdda26CCCCCCCCCCCCCCCCCCCCCCCCC86c4b", self.buildPath(["tree9", "dir003", "file002"]): "0cc03b3014CCCCCCCCCCCCCCCCCCCCCCCCCd26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a62CCCCCCCCCCCCCCCCCCCCCCCCC73847", self.buildPath(["tree9", "file003"]): "fae89085eeCCCCCCCCCCCCCCCCCCCCCCCCC769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_008(self): """ Test with a digest map containing both entries that are and are not in the list, with matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531c7e897cd3df90ed76355de7e21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir003", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir003", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file003"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(3, count) self.assertEqual(12, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_009(self): """ Test with a digest map containing both entries that are and are not in the list, with matching and non-matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531AAAAAAAAAAAAAAAAAAAAAAAe21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir003", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir003", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file003"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) count = backupList.removeUnchanged(digestMap) self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(2, count) self.assertEqual(13, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) def testRemoveUnchanged_010(self): """ Test on an empty list with an empty digest map. """ digestMap = {} backupList = BackupFileList() self.assertEqual(0, len(backupList)) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(0, len(backupList)) self.assertEqual(0, len(newDigest)) def testRemoveUnchanged_011(self): """ Test on an empty list with an non-empty digest map. """ digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531c7e897cd3df90ed76355de7e21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir002", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir002", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file002"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() self.assertEqual(0, len(backupList)) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(0, len(backupList)) self.assertEqual(0, len(newDigest)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_012(self): """ Test on an non-empty list with an empty digest map. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = {} backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) self.assertEqual(6, len(newDigest)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", newDigest[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", newDigest[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", newDigest[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", newDigest[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", newDigest[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", newDigest[self.buildPath(["tree9", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_013(self): """ Test with a digest map containing only entries that are not in the list. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir003", "file001"]): "4ff529531c7e897cd3df90ed76355de7e21e77ee", self.buildPath(["tree9", "dir003", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir004", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir004", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file003"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file004"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) self.assertEqual(6, len(newDigest)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", newDigest[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", newDigest[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", newDigest[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", newDigest[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", newDigest[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", newDigest[self.buildPath(["tree9", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_014(self): """ Test with a digest map containing only entries that are in the list, with non-matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531c7e8AAAAAAAAAAAAAAAAAA7e21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecAAAAAAAAAAAAAAAAAA95d1d6cba", self.buildPath(["tree9", "dir002", "file001"]): "2f68cdda26b64AAAAAAAAAAAAAAAAAA5b8786c4b", self.buildPath(["tree9", "dir002", "file002"]): "0cc03b3014d1cAAAAAAAAAAAAAAAAAA5d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237aAAAAAAAAAAAAAAAAAA555973847", self.buildPath(["tree9", "file002"]): "fae89085ee97bAAAAAAAAAAAAAAAAAAbb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) self.assertEqual(6, len(newDigest)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", newDigest[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", newDigest[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", newDigest[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", newDigest[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", newDigest[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", newDigest[self.buildPath(["tree9", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_015(self): """ Test with a digest map containing only entries that are in the list, with matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531c7e897cd3df90ed76355de7e21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir002", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir002", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file002"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(6, count) self.assertEqual(9, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) self.assertEqual(6, len(newDigest)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", newDigest[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", newDigest[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", newDigest[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", newDigest[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", newDigest[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", newDigest[self.buildPath(["tree9", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_016(self): """ Test with a digest map containing both entries that are and are not in the list, with non-matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531cCCCCCCCCCCCCCCCCCCCCCCCCCe77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a2CCCCCCCCCCCCCCCCCCCCCCCCCd6cba", self.buildPath(["tree9", "dir003", "file001"]): "2f68cdda26CCCCCCCCCCCCCCCCCCCCCCCCC86c4b", self.buildPath(["tree9", "dir003", "file002"]): "0cc03b3014CCCCCCCCCCCCCCCCCCCCCCCCCd26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a62CCCCCCCCCCCCCCCCCCCCCCCCC73847", self.buildPath(["tree9", "file003"]): "fae89085eeCCCCCCCCCCCCCCCCCCCCCCCCC769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(0, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) self.assertEqual(6, len(newDigest)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", newDigest[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", newDigest[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", newDigest[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", newDigest[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", newDigest[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", newDigest[self.buildPath(["tree9", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_017(self): """ Test with a digest map containing both entries that are and are not in the list, with matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531c7e897cd3df90ed76355de7e21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir003", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir003", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file003"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(3, count) self.assertEqual(12, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) self.assertEqual(6, len(newDigest)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", newDigest[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", newDigest[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", newDigest[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", newDigest[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", newDigest[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", newDigest[self.buildPath(["tree9", "file002"])]) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testRemoveUnchanged_018(self): """ Test with a digest map containing both entries that are and are not in the list, with matching and non-matching digests. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) digestMap = { self.buildPath(["tree9", "dir001", "file001"]): "4ff529531AAAAAAAAAAAAAAAAAAAAAAAe21e77ee", self.buildPath(["tree9", "dir001", "file002"]): "9d473094a22ecf2ae299c25932c941795d1d6cba", self.buildPath(["tree9", "dir003", "file001"]): "2f68cdda26b643ca0e53be6348ae1255b8786c4b", self.buildPath(["tree9", "dir003", "file002"]): "0cc03b3014d1ca7188264677cf01f015d72d26cb", self.buildPath(["tree9", "file001"]): "3ef0b16a6237af9200b7a46c1987d6a555973847", self.buildPath(["tree9", "file003"]): "fae89085ee97b57ccefa7e30346c573bb0a769db", } backupList = BackupFileList() count = backupList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) (count, newDigest) = backupList.removeUnchanged(digestMap, captureDigest=True) # pylint: disable=W0633 self.assertTrue(isinstance(backupList, BackupFileList)) # make sure we just replaced it self.assertEqual(2, count) self.assertEqual(13, len(backupList)) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in backupList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in backupList) self.assertTrue(self.buildPath(["tree9", "file002"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link001"]) in backupList) self.assertTrue(self.buildPath(["tree9", "link002"]) in backupList) self.assertEqual(6, len(newDigest)) self.assertEqual("4ff529531c7e897cd3df90ed76355de7e21e77ee", newDigest[self.buildPath(["tree9", "dir001", "file001"])]) self.assertEqual("9d473094a22ecf2ae299c25932c941795d1d6cba", newDigest[self.buildPath(["tree9", "dir001", "file002"])]) self.assertEqual("2f68cdda26b643ca0e53be6348ae1255b8786c4b", newDigest[self.buildPath(["tree9", "dir002", "file001"])]) self.assertEqual("0cc03b3014d1ca7188264677cf01f015d72d26cb", newDigest[self.buildPath(["tree9", "dir002", "file002"])]) self.assertEqual("3ef0b16a6237af9200b7a46c1987d6a555973847", newDigest[self.buildPath(["tree9", "file001"])]) self.assertEqual("fae89085ee97b57ccefa7e30346c573bb0a769db", newDigest[self.buildPath(["tree9", "file002"])]) ######################### # Test _generateDigest() ######################### # pylint: disable=E1101 def testGenerateDigest_001(self): """ Test that _generateDigest gives back same result as the slower simplistic implementation for a set of files (just using all of the resource files). """ for key in list(self.resources.keys()): path = self.resources[key] with open(path, mode="rb") as f: # because generateDigest also uses "rb" digest1 = hashlib.sha1(f.read()).hexdigest() digest2 = BackupFileList._generateDigest(path) self.assertEqual(digest1, digest2, "Digest for %s varies: [%s] vs [%s]." % (path, digest1, digest2)) ########################## # TestPurgeItemList class ########################## class TestPurgeItemList(unittest.TestCase): """Tests for the PurgeItemList class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) def pathPattern(self, path): """Returns properly-escaped regular expression pattern matching the indicated path.""" return ".*%s.*" % path.replace("\\", "\\\\") ######################## # Test addDirContents() ######################## def testAddDirContents_001(self): """ Attempt to add a directory that doesn't exist; no exclusions. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_002(self): """ Attempt to add a file; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_003(self): """ Attempt to add a soft link; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() count = purgeList.addDir(path) self.assertEqual(1, count) self.assertEqual([path], purgeList) def testAddDirContents_004(self): """ Attempt to add an empty directory containing ignore file; no exclusions. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_005(self): """ Attempt to add an empty directory; no exclusions. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_006(self): """ Attempt to add an non-empty directory containing ignore file; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_007(self): """ Attempt to add an non-empty directory; no exclusions. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(7, count) self.assertEqual(7, len(purgeList)) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in purgeList) def testAddDirContents_008(self): """ Attempt to add a directory that doesn't exist; excludeFiles set. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludeFiles = True self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_009(self): """ Attempt to add a file; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludeFiles = True self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_010(self): """ Attempt to add a soft link; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludeFiles = True self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludeFiles = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_011(self): """ Attempt to add an empty directory containing ignore file; excludeFiles set. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeFiles = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_012(self): """ Attempt to add an empty directory; excludeFiles set. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludeFiles = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_013(self): """ Attempt to add an non-empty directory containing ignore file; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeFiles = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_014(self): """ Attempt to add an non-empty directory; excludeFiles set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludeFiles = True count = purgeList.addDirContents(path) self.assertEqual(4, count) self.assertEqual(4, len(purgeList)) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in purgeList) def testAddDirContents_015(self): """ Attempt to add a directory that doesn't exist; excludeDirs set. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludeDirs = True self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_016(self): """ Attempt to add a file; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludeDirs = True self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_017(self): """ Attempt to add a soft link; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludeDirs = True self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludeDirs = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_018(self): """ Attempt to add an empty directory containing ignore file; excludeDirs set. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeDirs = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_019(self): """ Attempt to add an empty directory; excludeDirs set. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludeDirs = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_020(self): """ Attempt to add an non-empty directory containing ignore file; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeDirs = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_021(self): """ Attempt to add an non-empty directory; excludeDirs set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludeDirs = True count = purgeList.addDirContents(path) self.assertEqual(3, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in purgeList) def testAddDirContents_023(self): """ Attempt to add a directory that doesn't exist; excludeLinks set. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludeLinks = True self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_024(self): """ Attempt to add a file; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludeLinks = True self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_025(self): """ Attempt to add a soft link; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludeLinks = True self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludeLinks = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_026(self): """ Attempt to add an empty directory containing ignore file; excludeLinks set. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeLinks = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_027(self): """ Attempt to add an empty directory; excludeLinks set. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludeLinks = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_028(self): """ Attempt to add an non-empty directory containing ignore file; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeLinks = True count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_029(self): """ Attempt to add an non-empty directory; excludeLinks set. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludeLinks = True count = purgeList.addDirContents(path) self.assertEqual(6, count) self.assertEqual(6, len(purgeList)) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in purgeList) def testAddDirContents_030(self): """ Attempt to add a directory that doesn't exist; with excludePaths including the path. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludePaths = [path] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_031(self): """ Attempt to add a file; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludePaths = [path] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_032(self): """ Attempt to add a soft link; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludePaths = [path] self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludePaths = [path] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_033(self): """ Attempt to add an empty directory containing ignore file; with excludePaths including the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludePaths = [path] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_034(self): """ Attempt to add an empty directory; with excludePaths including the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludePaths = [path] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_035(self): """ Attempt to add an non-empty directory containing ignore file; with excludePaths including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludePaths = [path] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_036(self): """ Attempt to add an non-empty directory; with excludePaths including the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludePaths = [path] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_037(self): """ Attempt to add a directory that doesn't exist; with excludePaths not including the path. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_038(self): """ Attempt to add a file; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_039(self): """ Attempt to add a soft link; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludePaths = [NOMATCH_PATH] self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludePaths = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_040(self): """ Attempt to add an empty directory containing ignore file; with excludePaths not including the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludePaths = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_041(self): """ Attempt to add an empty directory; with excludePaths not including the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludePaths = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_042(self): """ Attempt to add an non-empty directory containing ignore file; with excludePaths not including the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludePaths = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_043(self): """ Attempt to add an non-empty directory; with excludePaths not including the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludePaths = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(7, count) self.assertEqual(7, len(purgeList)) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in purgeList) def testAddDirContents_044(self): """ Attempt to add a directory that doesn't exist; with excludePatterns matching the path. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_045(self): """ Attempt to add a file; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_046(self): """ Attempt to add a soft link; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludePatterns = [self.pathPattern(path)] self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludePatterns = [self.pathPattern(path)] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_047(self): """ Attempt to add an empty directory containing ignore file; with excludePatterns matching the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludePatterns = [self.pathPattern(path)] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_048(self): """ Attempt to add an empty directory; with excludePatterns matching the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludePatterns = [self.pathPattern(path)] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_049(self): """ Attempt to add an non-empty directory containing ignore file; with excludePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludePatterns = [self.pathPattern(path)] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_050(self): """ Attempt to add an non-empty directory; with excludePatterns matching the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludePatterns = [self.pathPattern(path)] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_051(self): """ Attempt to add a directory that doesn't exist; with excludePatterns not matching the path. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_052(self): """ Attempt to add a file; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_053(self): """ Attempt to add a soft link; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludePatterns = [NOMATCH_PATH] self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludePatterns = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_054(self): """ Attempt to add an empty directory containing ignore file; with excludePatterns not matching the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludePatterns = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_055(self): """ Attempt to add an empty directory; with excludePatterns not matching the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludePatterns = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_056(self): """ Attempt to add an non-empty directory containing ignore file; with excludePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludePatterns = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_057(self): """ Attempt to add an non-empty directory; with excludePatterns not matching the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludePatterns = [NOMATCH_PATH] count = purgeList.addDirContents(path) self.assertEqual(7, count) self.assertEqual(7, len(purgeList)) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in purgeList) def testAddDirContents_058(self): """ Attempt to add a large tree with no exclusions. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(135, count) self.assertEqual(135, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002"]) in purgeList) def testAddDirContents_059(self): """ Attempt to add a large tree, with excludeFiles set. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() purgeList.excludeFiles = True count = purgeList.addDirContents(path) self.assertEqual(41, count) self.assertEqual(41, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002"]) in purgeList) def testAddDirContents_060(self): """ Attempt to add a large tree, with excludeDirs set. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() purgeList.excludeDirs = True count = purgeList.addDirContents(path) self.assertEqual(94, count) self.assertEqual(94, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_061(self): """ Attempt to add a large tree, with excludeLinks set. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() purgeList.excludeLinks = True count = purgeList.addDirContents(path) self.assertEqual(95, count) self.assertEqual(95, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) def testAddDirContents_062(self): """ Attempt to add a large tree, with excludePaths set to exclude some entries. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() purgeList.excludePaths = [ self.buildPath(["tree6", "dir001", "dir002"]), self.buildPath(["tree6", "dir002", "dir001", "dir001"]), self.buildPath(["tree6", "dir003", "dir002", "file001"]), self.buildPath(["tree6", "dir003", "dir002", "file002"]), ] count = purgeList.addDirContents(path) self.assertEqual(124, count) self.assertEqual(124, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_063(self): """ Attempt to add a large tree, with excludePatterns set to exclude some entries. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() purgeList.excludePatterns = [".*file001.*", r".*tree6\/dir002\/dir001.*"] count = purgeList.addDirContents(path) self.assertEqual(107, count) self.assertEqual(107, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002"]) in purgeList) def testAddDirContents_064(self): """ Attempt to add a large tree, with ignoreFile set to exclude some directories. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" count = purgeList.addDirContents(path) self.assertEqual(78, count) self.assertEqual(78, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002"]) in purgeList) def testAddDirContents_065(self): """ Attempt to add a link to a file. """ self.extractTar("tree9") path = self.buildPath(["tree9", "dir002", "link003"]) purgeList = PurgeItemList() self.assertRaises(ValueError, purgeList.addDirContents, path) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_066(self): """ Attempt to add a link to a directory (which should add its contents). """ self.extractTar("tree9") path = self.buildPath(["tree9", "link002"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(purgeList)) self.assertTrue(self.buildPath(["tree9", "link002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "link002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "link002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "link002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "link002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "link002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "link002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "link002", "link004"]) in purgeList) def testAddDirContents_067(self): """ Attempt to add an invalid link (i.e. a link that points to something that doesn't exist). """ self.extractTar("tree10") path = self.buildPath(["tree10", "link001"]) purgeList = PurgeItemList() self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_068(self): """ Attempt to add directory containing an invalid link (i.e. a link that points to something that doesn't exist). """ self.extractTar("tree10") path = self.buildPath(["tree10"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(2, count) self.assertEqual(2, len(purgeList)) self.assertTrue(self.buildPath(["tree10", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree10", "dir002"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_069(self): """ Attempt to add a directory containing items with spaces. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(15, count) self.assertEqual(15, len(purgeList)) self.assertTrue(self.buildPath(["tree11", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in purgeList) def testAddDirContents_070(self): """ Attempt to add a directory which has a name containing spaces. """ self.extractTar("tree11") path = self.buildPath(["tree11", "dir with spaces"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(4, count) self.assertEqual(4, len(purgeList)) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in purgeList) def testAddDirContents_071(self): """ Attempt to add a directory which has a UTF-8 filename in it. """ self.extractTar("tree12") path = self.buildPath(["tree12", "unicode"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(5, count) self.assertEqual(5, len(purgeList)) self.assertTrue(self.buildPath(["tree12", "unicode", "README.strange-name"]) in purgeList) self.assertTrue(self.buildPath(["tree12", "unicode", "utflist.long.gz"]) in purgeList) self.assertTrue(self.buildPath(["tree12", "unicode", "utflist.cp437.gz"]) in purgeList) self.assertTrue(self.buildPath(["tree12", "unicode", "utflist.short.gz"]) in purgeList) self.assertTrue(self.buildPath(["tree12", "unicode", encodePath(b"\xe2\x99\xaa\xe2\x99\xac")]) in purgeList) def testAddDirContents_072(self): """ Attempt to add a directory which has several UTF-8 filenames in it. This test data was taken from Rick Lowe's problems around the release of v1.10. I don't run the test for Darwin (Mac OS X) because the tarball isn't valid there. All of the tests with unicode paths were incredibly painful to get working with Python 3, but these tests in particular were difficult, because character 0x82 is not a valid UTF-8 character. The key is was to get the filename into the same encoding used by methods like os.listdir(), which uses a "surrogateescape" fallback for encoding filenames. Once I switched encodePath to do the same thing, this test started passing. There's apparently no other way to represent filenames like this. """ if not platformMacOsX(): self.extractTar("tree13") path = self.buildPath(["tree13"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(10, count) self.assertEqual(10, len(purgeList)) self.assertTrue(self.buildPath(["tree13", encodePath(b"Les mouvements de r\x82forme.doc")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l'\x82nonc\x82.sxw")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l\x82onard - renvois et bibliographie.sxw")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l\x82onard copie finale.sxw")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l\x82onard de vinci - page titre.sxw")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"l\x82onard de vinci.sxw")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"Rammstein - B\x81ck Dich.mp3")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"megaherz - Glas Und Tr\x84nen.mp3")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"Megaherz - Mistst\x81ck.MP3")]) in purgeList) self.assertTrue(self.buildPath(["tree13", encodePath(b"Rammstein - Mutter - B\x94se.mp3")]) in purgeList) def testAddDirContents_073(self): """ Attempt to add a directory that doesn't exist; with excludeBasenamePatterns matching the path. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [INVALID_FILE] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_074(self): """ Attempt to add a file; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [ "file001", ] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_075(self): """ Attempt to add a soft link; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [ "link001", ] self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [ "link001", ] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_076(self): """ Attempt to add an empty directory containing ignore file; with excludeBasenamePatterns matching the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeBasenamePatterns = [ "dir001", ] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_077(self): """ Attempt to add an empty directory; with excludeBasenamePatterns matching the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [ "dir001", ] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_078(self): """ Attempt to add an non-empty directory containing ignore file; with excludeBasenamePatterns matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeBasenamePatterns = [ "dir008", ] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_079(self): """ Attempt to add an non-empty directory; with excludeBasenamePatterns matching the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [ "dir001", ] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_080(self): """ Attempt to add a directory that doesn't exist; with excludeBasenamePatterns not matching the path. """ path = self.buildPath([INVALID_FILE]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_081(self): """ Attempt to add a file; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "file001"]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, purgeList.addDirContents, path) def testAddDirContents_082(self): """ Attempt to add a soft link; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "link001"]) # link to a file purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [NOMATCH_BASENAME] self.assertRaises(ValueError, purgeList.addDirContents, path) path = self.buildPath(["tree5", "dir002", "link001"]) # link to a dir purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_083(self): """ Attempt to add an empty directory containing ignore file; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree7") path = self.buildPath(["tree7", "dir001"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_084(self): """ Attempt to add an empty directory; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree8") path = self.buildPath(["tree8", "dir001"]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_085(self): """ Attempt to add an non-empty directory containing ignore file; with excludeBasenamePatterns not matching the path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir008"]) purgeList = PurgeItemList() purgeList.ignoreFile = "ignore" purgeList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = purgeList.addDirContents(path) self.assertEqual(0, count) self.assertEqual([], purgeList) def testAddDirContents_086(self): """ Attempt to add an non-empty directory; with excludeBasenamePatterns not matching the main directory path. """ self.extractTar("tree5") path = self.buildPath(["tree5", "dir001"]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [NOMATCH_BASENAME] count = purgeList.addDirContents(path) self.assertEqual(7, count) self.assertEqual(7, len(purgeList)) self.assertTrue(self.buildPath(["tree5", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "dir004"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree5", "dir001", "link001"]) in purgeList) def testAddDirContents_087(self): """ Attempt to add a large tree, with excludeBasenamePatterns set to exclude some entries. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = [ "file001", "dir001", ] count = purgeList.addDirContents(path) self.assertEqual(63, count) self.assertEqual(63, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002"]) in purgeList) def testAddDirContents_088(self): """ Attempt to add a large tree, with excludeBasenamePatterns set to exclude some entries. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() purgeList.excludeBasenamePatterns = ["file001", "dir001"] count = purgeList.addDirContents(path) self.assertEqual(63, count) self.assertEqual(63, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002"]) in purgeList) def testAddDirContents_089(self): """ Attempt to add a large tree with no exclusions """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path) self.assertEqual(135, count) self.assertEqual(135, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_090(self): """ Attempt to add a directory with linkDepth=1. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=1) self.assertEqual(164, count) self.assertEqual(164, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_091(self): """ Attempt to add a directory with linkDepth=2. """ self.extractTar("tree6") path = self.buildPath(["tree6"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=2) self.assertEqual(240, count) self.assertEqual(240, len(purgeList)) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir001", "link001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir001", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "dir003", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link002", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir002", "link005", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "dir002", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file008"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "dir003", "link004", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "dir002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "dir002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "dir003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file006"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "file007"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "ignore"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link002", "link001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree6", "link001"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_092(self): """ Attempt to add a directory with linkDepth=0, dereference=False. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=0, dereference=False) self.assertEqual(11, count) self.assertEqual(11, len(purgeList)) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_093(self): """ Attempt to add a directory with linkDepth=1, dereference=False. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=1, dereference=False) self.assertEqual(15, count) self.assertEqual(15, len(purgeList)) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_094(self): """ Attempt to add a directory with linkDepth=2, dereference=False. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=2, dereference=False) self.assertEqual(19, count) self.assertEqual(19, len(purgeList)) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "link002"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_095(self): """ Attempt to add a directory with linkDepth=3, dereference=False. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=3, dereference=False) self.assertEqual(19, count) self.assertEqual(19, len(purgeList)) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "link001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003", "link002", "link002"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_096(self): """ Attempt to add a directory with linkDepth=0, dereference=True. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=0, dereference=True) self.assertEqual(11, count) self.assertEqual(11, len(purgeList)) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_097(self): """ Attempt to add a directory with linkDepth=1, dereference=True. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=1, dereference=True) self.assertEqual(19, count) self.assertEqual(19, len(purgeList)) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "link002"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_098(self): """ Attempt to add a directory with linkDepth=2, dereference=True. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=2, dereference=True) self.assertEqual(31, count) self.assertEqual(31, len(purgeList)) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir002", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir004", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir004", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir004", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir006"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir006", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir006", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir006", "link002"]) in purgeList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddDirContents_099(self): """ Attempt to add a directory with linkDepth=3, dereference=True. """ self.extractTar("tree22") path = self.buildPath(["tree22", "dir003"]) purgeList = PurgeItemList() count = purgeList.addDirContents(path, linkDepth=3, dereference=True) self.assertEqual(34, count) self.assertEqual(34, len(purgeList)) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "dir001", "link004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir003", "link003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir001", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir001", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir001", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir002", "file004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir002", "file005"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir002", "file009"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir004"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir004", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir004", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir004", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir005", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir006"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir006", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir006", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir006", "link002"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir007"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir007", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree22", "dir008", "file001"]) in purgeList) #################### # Test removeAged() #################### def testRemoveYoungFiles_001(self): """ Test on an empty list, daysOld < 0. """ daysOld = -1 purgeList = PurgeItemList() self.assertRaises(ValueError, purgeList.removeYoungFiles, daysOld) def testRemoveYoungFiles_002(self): """ Test on a non-empty list, daysOld < 0. """ daysOld = -1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree1"])) purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) self.assertRaises(ValueError, purgeList.removeYoungFiles, daysOld) def testRemoveYoungFiles_003(self): """ Test on an empty list, daysOld = 0 """ daysOld = 0 purgeList = PurgeItemList() count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_004(self): """ Test on a non-empty list containing only directories, daysOld = 0. """ daysOld = 0 self.extractTar("tree2") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree2"])) purgeList.addDir(self.buildPath(["tree2", "dir001"])) purgeList.addDir(self.buildPath(["tree2", "dir002"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree2"]) in purgeList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in purgeList) def testRemoveYoungFiles_005(self): """ Test on a non-empty list containing only links, daysOld = 0. """ daysOld = 0 self.extractTar("tree9") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree9", "link001"])) purgeList.addDir(self.buildPath(["tree9", "dir002", "link001"])) purgeList.addFile(self.buildPath(["tree9", "dir002", "link004"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree9", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in purgeList) def testRemoveYoungFiles_006(self): """ Test on a non-empty list containing only non-existent files, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.append(self.buildPath(["tree1", "stuff001"])) # append, since it doesn't exist on disk purgeList.append(self.buildPath(["tree1", "stuff002"])) # append, since it doesn't exist on disk purgeList.append(self.buildPath(["tree1", "stuff003"])) # append, since it doesn't exist on disk count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree1", "stuff001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "stuff002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "stuff003"]) in purgeList) def testRemoveYoungFiles_007(self): """ Test on a non-empty list containing existing files "touched" to current time, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"])) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_008(self): """ Test on a non-empty list containing existing files "touched" to being 1 hour old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_1_HOUR) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_1_HOUR) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_009(self): """ Test on a non-empty list containing existing files "touched" to being 2 hours old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_2_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_2_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_010(self): """ Test on a non-empty list containing existing files "touched" to being 12 hours old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_12_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_12_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_011(self): """ Test on a non-empty list containing existing files "touched" to being 23 hours old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_23_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_23_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_012(self): """ Test on a non-empty list containing existing files "touched" to being 24 hours old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_24_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_24_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_013(self): """ Test on a non-empty list containing existing files "touched" to being 25 hours old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_25_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_25_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_014(self): """ Test on a non-empty list containing existing files "touched" to being 47 hours old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_47_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_47_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_015(self): """ Test on a non-empty list containing existing files "touched" to being 48 hours old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_48_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_48_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_016(self): """ Test on a non-empty list containing existing files "touched" to being 49 hours old, daysOld = 0. """ daysOld = 0 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_49_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_49_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file003"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_017(self): """ Test on an empty list, daysOld = 1 """ daysOld = 1 purgeList = PurgeItemList() count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_018(self): """ Test on a non-empty list containing only directories, daysOld = 1. """ daysOld = 1 self.extractTar("tree2") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree2"])) purgeList.addDir(self.buildPath(["tree2", "dir001"])) purgeList.addDir(self.buildPath(["tree2", "dir002"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree2"]) in purgeList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in purgeList) def testRemoveYoungFiles_019(self): """ Test on a non-empty list containing only links, daysOld = 1. """ daysOld = 1 self.extractTar("tree9") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree9", "link001"])) purgeList.addDir(self.buildPath(["tree9", "dir002", "link001"])) purgeList.addFile(self.buildPath(["tree9", "dir002", "link004"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree9", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in purgeList) def testRemoveYoungFiles_020(self): """ Test on a non-empty list containing only non-existent files, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.append(self.buildPath(["tree1", "stuff001"])) # append, since it doesn't exist on disk purgeList.append(self.buildPath(["tree1", "stuff002"])) # append, since it doesn't exist on disk purgeList.append(self.buildPath(["tree1", "stuff003"])) # append, since it doesn't exist on disk count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree1", "stuff001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "stuff002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "stuff003"]) in purgeList) def testRemoveYoungFiles_021(self): """ Test on a non-empty list containing existing files "touched" to current time, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"])) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_022(self): """ Test on a non-empty list containing existing files "touched" to being 1 hour old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_1_HOUR) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_1_HOUR) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_023(self): """ Test on a non-empty list containing existing files "touched" to being 2 hours old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_2_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_2_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_024(self): """ Test on a non-empty list containing existing files "touched" to being 12 hours old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_12_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_12_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_025(self): """ Test on a non-empty list containing existing files "touched" to being 23 hours old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_23_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_23_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_026(self): """ Test on a non-empty list containing existing files "touched" to being 24 hours old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_24_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_24_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(2, count) self.assertEqual(2, len(purgeList)) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_027(self): """ Test on a non-empty list containing existing files "touched" to being 25 hours old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_25_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_25_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(2, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_028(self): """ Test on a non-empty list containing existing files "touched" to being 47 hours old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_47_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_47_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(2, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_029(self): """ Test on a non-empty list containing existing files "touched" to being 48 hours old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_48_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_48_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(2, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_030(self): """ Test on a non-empty list containing existing files "touched" to being 49 hours old, daysOld = 1. """ daysOld = 1 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_49_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_49_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(2, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_031(self): """ Test on an empty list, daysOld = 2 """ daysOld = 2 purgeList = PurgeItemList() count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_032(self): """ Test on a non-empty list containing only directories, daysOld = 2. """ daysOld = 2 self.extractTar("tree2") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree2"])) purgeList.addDir(self.buildPath(["tree2", "dir001"])) purgeList.addDir(self.buildPath(["tree2", "dir002"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree2"]) in purgeList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in purgeList) def testRemoveYoungFiles_033(self): """ Test on a non-empty list containing only links, daysOld = 2. """ daysOld = 2 self.extractTar("tree9") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree9", "link001"])) purgeList.addDir(self.buildPath(["tree9", "dir002", "link001"])) purgeList.addFile(self.buildPath(["tree9", "dir002", "link004"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree9", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in purgeList) def testRemoveYoungFiles_034(self): """ Test on a non-empty list containing only non-existent files, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.append(self.buildPath(["tree1", "stuff001"])) # append, since it doesn't exist on disk purgeList.append(self.buildPath(["tree1", "stuff002"])) # append, since it doesn't exist on disk purgeList.append(self.buildPath(["tree1", "stuff003"])) # append, since it doesn't exist on disk count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree1", "stuff001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "stuff002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "stuff003"]) in purgeList) def testRemoveYoungFiles_035(self): """ Test on a non-empty list containing existing files "touched" to current time, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"])) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_036(self): """ Test on a non-empty list containing existing files "touched" to being 1 hour old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_1_HOUR) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_1_HOUR) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_037(self): """ Test on a non-empty list containing existing files "touched" to being 2 hours old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_2_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_2_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_038(self): """ Test on a non-empty list containing existing files "touched" to being 12 hours old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_12_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_12_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_039(self): """ Test on a non-empty list containing existing files "touched" to being 23 hours old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_23_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_23_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_040(self): """ Test on a non-empty list containing existing files "touched" to being 24 hours old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_24_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_24_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_041(self): """ Test on a non-empty list containing existing files "touched" to being 25 hours old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_25_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_25_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_042(self): """ Test on a non-empty list containing existing files "touched" to being 47 hours old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_47_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_47_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_043(self): """ Test on a non-empty list containing existing files "touched" to being 48 hours old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_48_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_48_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(2, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_044(self): """ Test on a non-empty list containing existing files "touched" to being 49 hours old, daysOld = 2. """ daysOld = 2 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_49_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_49_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(2, count) self.assertTrue(self.buildPath(["tree1", "file001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "file004"]) in purgeList) def testRemoveYoungFiles_045(self): """ Test on an empty list, daysOld = 3 """ daysOld = 3 purgeList = PurgeItemList() count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_046(self): """ Test on a non-empty list containing only directories, daysOld = 3. """ daysOld = 3 self.extractTar("tree2") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree2"])) purgeList.addDir(self.buildPath(["tree2", "dir001"])) purgeList.addDir(self.buildPath(["tree2", "dir002"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree2"]) in purgeList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in purgeList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in purgeList) def testRemoveYoungFiles_047(self): """ Test on a non-empty list containing only links, daysOld = 3. """ daysOld = 3 self.extractTar("tree9") purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree9", "link001"])) purgeList.addDir(self.buildPath(["tree9", "dir002", "link001"])) purgeList.addFile(self.buildPath(["tree9", "dir002", "link004"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree9", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in purgeList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in purgeList) def testRemoveYoungFiles_048(self): """ Test on a non-empty list containing only non-existent files, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.append(self.buildPath(["tree1", "stuff001"])) # append, since it doesn't exist on disk purgeList.append(self.buildPath(["tree1", "stuff002"])) # append, since it doesn't exist on disk purgeList.append(self.buildPath(["tree1", "stuff003"])) # append, since it doesn't exist on disk count = purgeList.removeYoungFiles(daysOld) self.assertEqual(0, count) self.assertEqual(3, len(purgeList)) self.assertTrue(self.buildPath(["tree1", "stuff001"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "stuff002"]) in purgeList) self.assertTrue(self.buildPath(["tree1", "stuff003"]) in purgeList) def testRemoveYoungFiles_049(self): """ Test on a non-empty list containing existing files "touched" to current time, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"])) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"])) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_050(self): """ Test on a non-empty list containing existing files "touched" to being 1 hour old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_1_HOUR) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_1_HOUR) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_051(self): """ Test on a non-empty list containing existing files "touched" to being 2 hours old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_2_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_2_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_052(self): """ Test on a non-empty list containing existing files "touched" to being 12 hours old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_12_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_12_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_053(self): """ Test on a non-empty list containing existing files "touched" to being 23 hours old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_23_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_23_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_054(self): """ Test on a non-empty list containing existing files "touched" to being 24 hours old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_24_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_24_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_055(self): """ Test on a non-empty list containing existing files "touched" to being 25 hours old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_25_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_25_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_056(self): """ Test on a non-empty list containing existing files "touched" to being 47 hours old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_47_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_47_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_057(self): """ Test on a non-empty list containing existing files "touched" to being 48 hours old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_48_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_48_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) def testRemoveYoungFiles_058(self): """ Test on a non-empty list containing existing files "touched" to being 49 hours old, daysOld = 3. """ daysOld = 3 self.extractTar("tree1") purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) changeFileAge(self.buildPath(["tree1", "file001"]), AGE_49_HOURS) changeFileAge(self.buildPath(["tree1", "file002"])) changeFileAge(self.buildPath(["tree1", "file003"])) changeFileAge(self.buildPath(["tree1", "file004"]), AGE_49_HOURS) count = purgeList.removeYoungFiles(daysOld) self.assertEqual(4, count) self.assertEqual([], purgeList) #################### # Test purgeItems() #################### def testPurgeItems_001(self): """ Test with an empty list. """ purgeList = PurgeItemList() (files, dirs) = purgeList.purgeItems() self.assertEqual(0, files) self.assertEqual(0, dirs) def testPurgeItems_002(self): """ Test with a list containing only non-empty directories. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree9"])) purgeList.addDir(self.buildPath(["tree9", "dir001"])) purgeList.addDir(self.buildPath(["tree9", "dir002"])) (files, dirs) = purgeList.purgeItems() self.assertEqual(0, files) self.assertEqual(0, dirs) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testPurgeItems_003(self): """ Test with a list containing only empty directories. """ self.extractTar("tree2") path = self.buildPath(["tree2"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(11, count) self.assertEqual(11, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir003"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir004"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir005"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir006"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir007"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir008"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir009"]) in fsList) self.assertTrue(self.buildPath(["tree2", "dir010"]) in fsList) purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree2", "dir001"])) purgeList.addDir(self.buildPath(["tree2", "dir002"])) purgeList.addDir(self.buildPath(["tree2", "dir003"])) purgeList.addDir(self.buildPath(["tree2", "dir004"])) purgeList.addDir(self.buildPath(["tree2", "dir005"])) purgeList.addDir(self.buildPath(["tree2", "dir006"])) purgeList.addDir(self.buildPath(["tree2", "dir007"])) purgeList.addDir(self.buildPath(["tree2", "dir008"])) purgeList.addDir(self.buildPath(["tree2", "dir009"])) purgeList.addDir(self.buildPath(["tree2", "dir010"])) (files, dirs) = purgeList.purgeItems() self.assertEqual(0, files) self.assertEqual(10, dirs) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual(1, len(fsList)) self.assertTrue(self.buildPath(["tree2"]) in fsList) def testPurgeItems_004(self): """ Test with a list containing only files. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) purgeList.addFile(self.buildPath(["tree1", "file005"])) purgeList.addFile(self.buildPath(["tree1", "file006"])) purgeList.addFile(self.buildPath(["tree1", "file007"])) (files, dirs) = purgeList.purgeItems() self.assertEqual(7, files) self.assertEqual(0, dirs) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(1, count) self.assertEqual(1, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) def testPurgeItems_005(self): """ Test with a list containing a directory and some of the files in that directory. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree1"])) purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) (files, dirs) = purgeList.purgeItems() self.assertEqual(4, files) self.assertEqual(0, dirs) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(4, count) self.assertEqual(4, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) def testPurgeItems_006(self): """ Test with a list containing a directory and all of the files in that directory. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree1"])) purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) purgeList.addFile(self.buildPath(["tree1", "file005"])) purgeList.addFile(self.buildPath(["tree1", "file006"])) purgeList.addFile(self.buildPath(["tree1", "file007"])) (files, dirs) = purgeList.purgeItems() self.assertEqual(7, files) self.assertEqual(1, dirs) self.assertRaises(ValueError, fsList.addDirContents, path) self.assertTrue(not os.path.exists(path)) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testPurgeItems_007(self): """ Test with a list containing various kinds of entries, including links, files and directories. Make sure that removing a link doesn't remove the file the link points toward. """ self.extractTar("tree9") path = self.buildPath(["tree9"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(22, count) self.assertEqual(22, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree9", "dir001", "link001"])) purgeList.addDir(self.buildPath(["tree9", "dir002", "dir001"])) purgeList.addFile(self.buildPath(["tree9", "file001"])) (files, dirs) = purgeList.purgeItems() self.assertEqual(2, files) self.assertEqual(1, dirs) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(18, count) self.assertEqual(18, len(fsList)) self.assertTrue(self.buildPath(["tree9"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir001", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "file002"]) in fsList) self.assertTrue(os.path.islink(self.buildPath(["tree9", "dir002", "link001"]))) # won't be included in list, though self.assertTrue(self.buildPath(["tree9", "dir002", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree9", "dir002", "link004"]) in fsList) self.assertTrue(self.buildPath(["tree9", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree9", "link002"]) in fsList) def testPurgeItems_008(self): """ Test with a list containing non-existent entries. """ self.extractTar("tree1") path = self.buildPath(["tree1"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(8, count) self.assertEqual(8, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file004"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) purgeList = PurgeItemList() purgeList.addDir(self.buildPath(["tree1"])) purgeList.addFile(self.buildPath(["tree1", "file001"])) purgeList.addFile(self.buildPath(["tree1", "file002"])) purgeList.addFile(self.buildPath(["tree1", "file003"])) purgeList.addFile(self.buildPath(["tree1", "file004"])) purgeList.append(self.buildPath(["tree1", INVALID_FILE])) # bypass validations (files, dirs) = purgeList.purgeItems() self.assertEqual(4, files) self.assertEqual(0, dirs) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(4, count) self.assertEqual(4, len(fsList)) self.assertTrue(self.buildPath(["tree1"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file005"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file006"]) in fsList) self.assertTrue(self.buildPath(["tree1", "file007"]) in fsList) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testPurgeItems_009(self): """ Test with a list containing entries containing spaces. """ self.extractTar("tree11") path = self.buildPath(["tree11"]) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(16, count) self.assertEqual(16, len(fsList)) self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) purgeList = PurgeItemList() purgeList.addFile(self.buildPath(["tree11", "file with spaces"])) purgeList.addFile(self.buildPath(["tree11", "dir with spaces", "file with spaces"])) (files, dirs) = purgeList.purgeItems() self.assertEqual(2, files) self.assertEqual(0, dirs) fsList = FilesystemList() count = fsList.addDirContents(path) self.assertEqual(12, count) self.assertEqual(12, len(fsList)) self.assertTrue(self.buildPath(["tree11", "link with spaces"]) not in fsList) # file it points to was removed self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link002"]) not in fsList) # file it points to was removed self.assertTrue(self.buildPath(["tree11"]) in fsList) self.assertTrue(self.buildPath(["tree11", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "link003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file002"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir002", "file003"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "file001"]) in fsList) self.assertTrue(self.buildPath(["tree11", "dir with spaces", "link with spaces"]) in fsList) ###################### # TestFunctions class ###################### class TestFunctions(unittest.TestCase): """Tests for the various public functions.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname, within=None): """Extracts a tarfile with a particular name.""" if within is None: extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) else: path = pathJoin(self.tmpdir, within) os.mkdir(path) extractTar(path, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) ######################### # Test compareContents() ######################### def testCompareContents_001(self): """ Compare two empty directories. """ self.extractTar("tree2", within="path1") self.extractTar("tree2", within="path2") path1 = self.buildPath(["path1", "tree2", "dir001"]) path2 = self.buildPath(["path2", "tree2", "dir002"]) compareContents(path1, path2) compareContents(path1, path2, verbose=True) def testCompareContents_002(self): """ Compare one empty and one non-empty directory containing only directories. """ self.extractTar("tree2", within="path1") self.extractTar("tree2", within="path2") path1 = self.buildPath(["path1", "tree2", "dir001"]) path2 = self.buildPath(["path2", "tree2"]) compareContents(path1, path2) compareContents(path1, path2, verbose=True) def testCompareContents_003(self): """ Compare one empty and one non-empty directory containing only files. """ self.extractTar("tree2", within="path1") self.extractTar("tree1", within="path2") path1 = self.buildPath(["path1", "tree2", "dir001"]) path2 = self.buildPath(["path2", "tree1"]) self.assertRaises(ValueError, compareContents, path1, path2) self.assertRaises(ValueError, compareContents, path1, path2, verbose=True) def testCompareContents_004(self): """ Compare two directories containing only directories, same. """ self.extractTar("tree2", within="path1") self.extractTar("tree2", within="path2") path1 = self.buildPath(["path1", "tree2"]) path2 = self.buildPath(["path2", "tree2"]) compareContents(path1, path2) compareContents(path1, path2, verbose=True) def testCompareContents_005(self): """ Compare two directories containing only directories, different set. """ self.extractTar("tree2", within="path1") self.extractTar("tree3", within="path2") path1 = self.buildPath(["path1", "tree2"]) path2 = self.buildPath(["path2", "tree3"]) compareContents(path1, path2) # no error, since directories don't count compareContents(path1, path2, verbose=True) # no error, since directories don't count def testCompareContents_006(self): """ Compare two directories containing only files, same. """ self.extractTar("tree1", within="path1") self.extractTar("tree1", within="path2") path1 = self.buildPath(["path1", "tree1"]) path2 = self.buildPath(["path2", "tree1"]) compareContents(path1, path2) compareContents(path1, path2, verbose=True) def testCompareContents_007(self): """ Compare two directories containing only files, different contents. """ self.extractTar("tree1", within="path1") self.extractTar("tree1", within="path2") path1 = self.buildPath(["path1", "tree1"]) path2 = self.buildPath(["path2", "tree1"]) with open(self.buildPath(["path1", "tree1", "file004"]), "a") as f: f.write("BOGUS") # change content self.assertRaises(ValueError, compareContents, path1, path2) self.assertRaises(ValueError, compareContents, path1, path2, verbose=True) def testCompareContents_008(self): """ Compare two directories containing only files, different set. """ self.extractTar("tree1", within="path1") self.extractTar("tree7", within="path2") path1 = self.buildPath(["path1", "tree1"]) path2 = self.buildPath(["path2", "tree7", "dir001"]) self.assertRaises(ValueError, compareContents, path1, path2) self.assertRaises(ValueError, compareContents, path1, path2, verbose=True) def testCompareContents_009(self): """ Compare two directories containing files and directories, same. """ self.extractTar("tree9", within="path1") self.extractTar("tree9", within="path2") path1 = self.buildPath(["path1", "tree9"]) path2 = self.buildPath(["path2", "tree9"]) compareContents(path1, path2) compareContents(path1, path2, verbose=True) def testCompareContents_010(self): """ Compare two directories containing files and directories, different contents. """ self.extractTar("tree9", within="path1") self.extractTar("tree9", within="path2") path1 = self.buildPath(["path1", "tree9"]) path2 = self.buildPath(["path2", "tree9"]) with open(self.buildPath(["path2", "tree9", "dir001", "file002"]), "a") as f: f.write("whoops") # change content self.assertRaises(ValueError, compareContents, path1, path2) self.assertRaises(ValueError, compareContents, path1, path2, verbose=True) def testCompareContents_011(self): """ Compare two directories containing files and directories, different set. """ self.extractTar("tree9", within="path1") self.extractTar("tree6", within="path2") path1 = self.buildPath(["path1", "tree9"]) path2 = self.buildPath(["path2", "tree6"]) self.assertRaises(ValueError, compareContents, path1, path2) self.assertRaises(ValueError, compareContents, path1, path2, verbose=True) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1609564 cedar_backup3-3.8.1/tests/test_knapsack.py0000644000000000000000000030215314567004737015526 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2005,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests knapsack functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/knapsack.py. Code Coverage ============= This module contains individual tests for each of the public functions implemented in knapsack.py: ``firstFit()``, ``bestFit()``, ``worstFit()`` and ``alternateFit()``. Note that the tests for each function are pretty much identical and so there's pretty much code duplication. In production code, I would argue that this implies some refactoring is needed. In here, however, I prefer having lots of individual test cases even if there is duplication, because I think this makes it easier to judge the extent of a problem when one exists. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a KNAPSACKTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## # Import standard modules import unittest from CedarBackup3.knapsack import alternateFit, bestFit, firstFit, worstFit from CedarBackup3.testutil import configureLogging ####################################################################### # Module-wide configuration and constants ####################################################################### # These all have random letters for keys because the original data had a,b,c,d, # etc. in ascending order, which actually masked a sorting bug in the implementation. ITEMS_01 = {} ITEMS_02 = { "z": 0, "^": 0, "3": 0, "(": 0, "[": 0, "/": 0, "a": 0, "r": 0, } ITEMS_03 = { "k": 0, "*": 1, "u": 10, "$": 100, "h": 1000, "?": 10000, "b": 100000, "s": 1000000, } ITEMS_04 = { "l": 1000000, "G": 100000, "h": 10000, "#": 1000, "a": 100, "'": 10, "c": 1, "t": 0, } ITEMS_05 = { "n": 1, "N": 1, "z": 1, "@": 1, "c": 1, "h": 1, "d": 1, "u": 1, } ITEMS_06 = { "o": 10, "b": 10, "G": 10, "+": 10, "B": 10, "O": 10, "e": 10, "v": 10, } ITEMS_07 = { "$": 100, "K": 100, "f": 100, "=": 100, "n": 100, "I": 100, "F": 100, "w": 100, } ITEMS_08 = { "y": 1000, "C": 1000, "s": 1000, "f": 1000, "a": 1000, "U": 1000, "g": 1000, "x": 1000, } ITEMS_09 = { "7": 10000, "d": 10000, "f": 10000, "g": 10000, "t": 10000, "l": 10000, "h": 10000, "y": 10000, } ITEMS_10 = { "5": 100000, "#": 100000, "l": 100000, "t": 100000, "6": 100000, "T": 100000, "i": 100000, "z": 100000, } ITEMS_11 = { "t": 1, "d": 1, "k": 100000, "l": 100000, "7": 100000, "G": 100000, "j": 1, "1": 1, } ITEMS_12 = { "a": 10, "e": 10, "M": 100000, "u": 100000, "y": 100000, "f": 100000, "k": 10, "2": 10, } ITEMS_13 = { "n": 100, "p": 100, "b": 100000, "i": 100000, "$": 100000, "/": 100000, "l": 100, "3": 100, } ITEMS_14 = { "b": 1000, ":": 1000, "e": 100000, "O": 100000, "o": 100000, "#": 100000, "m": 1000, "4": 1000, } ITEMS_15 = { "c": 1, "j": 1, "e": 1, "H": 100000, "n": 100000, "h": 1, "N": 1, "5": 1, } ITEMS_16 = { "a": 10, "M": 10, "%": 10, "'": 100000, "l": 100000, "?": 10, "o": 10, "6": 10, } ITEMS_17 = { "h": 100, "z": 100, "(": 100, "?": 100000, "k": 100000, "|": 100, "p": 100, "7": 100, } ITEMS_18 = { "[": 1000, "l": 1000, "*": 1000, "/": 100000, "z": 100000, "|": 1000, "q": 1000, "h": 1000, } # This is a more realistic example, taken from tree9.tar.gz ITEMS_19 = { "dir001/file001": 243, "dir001/file002": 268, "dir002/file001": 134, "dir002/file002": 74, "file001": 155, "file002": 242, "link001": 0, "link002": 0, } ####################################################################### # Utility functions ####################################################################### def buildItemDict(origDict): """ Creates an item dictionary suitable for passing to a knapsack function. The knapsack functions take a dictionary, keyed on item, of (item, size) tuples. This function converts a simple item/size dictionary to a knapsack dictionary. It exists for convenience. Args: origDict (Simple dictionary mapping item to size, like ``ITEMS_02``): Dictionary to convert Returns: Dictionary suitable for passing to a knapsack function """ itemDict = {} for key in list(origDict.keys()): itemDict[key] = (key, origDict[key]) return itemDict ####################################################################### # Test Case Classes ####################################################################### ##################### # TestKnapsack class ##################### class TestKnapsack(unittest.TestCase): """Tests for the various knapsack functions.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ################################ # Tests for firstFit() function ################################ def testFirstFit_001(self): """ Test firstFit() behavior for an empty items dictionary, zero capacity. """ items = buildItemDict(ITEMS_01) capacity = 0 result = firstFit(items, capacity) self.assertEqual(([], 0), result) def testFirstFit_002(self): """ Test firstFit() behavior for an empty items dictionary, non-zero capacity. """ items = buildItemDict(ITEMS_01) capacity = 10000 result = firstFit(items, capacity) self.assertEqual(([], 0), result) def testFirstFit_003(self): """ Test firstFit() behavior for an non-empty items dictionary, zero capacity. """ items = buildItemDict(ITEMS_03) capacity = 0 result = firstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_04) capacity = 0 result = firstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_13) capacity = 0 result = firstFit(items, capacity) self.assertEqual(([], 0), result) def testFirstFit_004(self): """ Test firstFit() behavior for non-empty items dictionary with zero-sized items, zero capacity. """ items = buildItemDict(ITEMS_03) capacity = 0 result = firstFit(items, capacity) self.assertEqual(([], 0), result) def testFirstFit_005(self): """ Test firstFit() behavior for items dictionary where only one item fits. """ items = buildItemDict(ITEMS_05) capacity = 1 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(1, result[1]) items = buildItemDict(ITEMS_06) capacity = 10 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(10, result[1]) items = buildItemDict(ITEMS_07) capacity = 100 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(100, result[1]) items = buildItemDict(ITEMS_08) capacity = 1000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(1000, result[1]) items = buildItemDict(ITEMS_09) capacity = 10000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(10000, result[1]) items = buildItemDict(ITEMS_10) capacity = 100000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(100000, result[1]) def testFirstFit_006(self): """ Test firstFit() behavior for items dictionary where only 25% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 2 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2, result[1]) items = buildItemDict(ITEMS_06) capacity = 25 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20, result[1]) items = buildItemDict(ITEMS_07) capacity = 250 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200, result[1]) items = buildItemDict(ITEMS_08) capacity = 2500 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2000, result[1]) items = buildItemDict(ITEMS_09) capacity = 25000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20000, result[1]) items = buildItemDict(ITEMS_10) capacity = 250000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200000, result[1]) items = buildItemDict(ITEMS_11) capacity = 2 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2, result[1]) items = buildItemDict(ITEMS_12) capacity = 25 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20, result[1]) items = buildItemDict(ITEMS_13) capacity = 250 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200, result[1]) items = buildItemDict(ITEMS_14) capacity = 2500 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2000, result[1]) def testFirstFit_007(self): """ Test firstFit() behavior for items dictionary where only 50% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 4 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_06) capacity = 45 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_07) capacity = 450 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_08) capacity = 4500 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) items = buildItemDict(ITEMS_09) capacity = 45000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40000, result[1]) items = buildItemDict(ITEMS_10) capacity = 450000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400000, result[1]) items = buildItemDict(ITEMS_11) capacity = 4 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_12) capacity = 45 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_13) capacity = 450 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_14) capacity = 4500 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) def testFirstFit_008(self): """ Test firstFit() behavior for items dictionary where only 75% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 6 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6, result[1]) items = buildItemDict(ITEMS_06) capacity = 65 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60, result[1]) items = buildItemDict(ITEMS_07) capacity = 650 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600, result[1]) items = buildItemDict(ITEMS_08) capacity = 6500 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6000, result[1]) items = buildItemDict(ITEMS_09) capacity = 65000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60000, result[1]) items = buildItemDict(ITEMS_10) capacity = 650000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600000, result[1]) items = buildItemDict(ITEMS_15) capacity = 7 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6, result[1]) items = buildItemDict(ITEMS_16) capacity = 65 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60, result[1]) items = buildItemDict(ITEMS_17) capacity = 650 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600, result[1]) items = buildItemDict(ITEMS_18) capacity = 6500 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6000, result[1]) def testFirstFit_009(self): """ Test firstFit() behavior for items dictionary where all items individually exceed the capacity. """ items = buildItemDict(ITEMS_06) capacity = 9 result = firstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_07) capacity = 99 result = firstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_08) capacity = 999 result = firstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_09) capacity = 9999 result = firstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_10) capacity = 99999 result = firstFit(items, capacity) self.assertEqual(([], 0), result) def testFirstFit_010(self): """ Test firstFit() behavior for items dictionary where first half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_04) capacity = 200 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(111, result[1]) def testFirstFit_011(self): """ Test firstFit() behavior for items dictionary where middle half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_11) capacity = 5 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_12) capacity = 50 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_13) capacity = 500 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_14) capacity = 5000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) def testFirstFit_012(self): """ Test firstFit() behavior for items dictionary where second half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_03) capacity = 200 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(111, result[1]) def testFirstFit_013(self): """ Test firstFit() behavior for items dictionary where first half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_04) capacity = 50 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testFirstFit_014(self): """ Test firstFit() behavior for items dictionary where middle half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_11) capacity = 3 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_12) capacity = 35 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_13) capacity = 350 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_14) capacity = 3500 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testFirstFit_015(self): """ Test firstFit() behavior for items dictionary where second half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_03) capacity = 50 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testFirstFit_016(self): """ Test firstFit() behavior for items dictionary where all items fit. """ items = buildItemDict(ITEMS_02) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(0, result[1]) items = buildItemDict(ITEMS_03) capacity = 2000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(1111111, result[1]) items = buildItemDict(ITEMS_04) capacity = 2000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(1111111, result[1]) items = buildItemDict(ITEMS_05) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(8, result[1]) items = buildItemDict(ITEMS_06) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(80, result[1]) items = buildItemDict(ITEMS_07) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(800, result[1]) items = buildItemDict(ITEMS_08) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(8000, result[1]) items = buildItemDict(ITEMS_09) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(80000, result[1]) items = buildItemDict(ITEMS_10) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(800000, result[1]) items = buildItemDict(ITEMS_11) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400004, result[1]) items = buildItemDict(ITEMS_12) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400040, result[1]) items = buildItemDict(ITEMS_13) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400400, result[1]) items = buildItemDict(ITEMS_14) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(404000, result[1]) items = buildItemDict(ITEMS_15) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200006, result[1]) items = buildItemDict(ITEMS_16) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200060, result[1]) items = buildItemDict(ITEMS_17) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200600, result[1]) items = buildItemDict(ITEMS_18) capacity = 1000000 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(206000, result[1]) def testFirstFit_017(self): """ Test firstFit() behavior for a more realistic set of items """ items = buildItemDict(ITEMS_19) capacity = 760 result = firstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) # Unfortunately, can't test any more than this, since dict keys come out in random order ############################### # Tests for bestFit() function ############################### def testBestFit_001(self): """ Test bestFit() behavior for an empty items dictionary, zero capacity. """ items = buildItemDict(ITEMS_01) capacity = 0 result = bestFit(items, capacity) self.assertEqual(([], 0), result) def testBestFit_002(self): """ Test bestFit() behavior for an empty items dictionary, non-zero capacity. """ items = buildItemDict(ITEMS_01) capacity = 10000 result = bestFit(items, capacity) self.assertEqual(([], 0), result) def testBestFit_003(self): """ Test bestFit() behavior for an non-empty items dictionary, zero capacity. """ items = buildItemDict(ITEMS_03) capacity = 0 result = bestFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_04) capacity = 0 result = bestFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_13) capacity = 0 result = bestFit(items, capacity) self.assertEqual(([], 0), result) def testBestFit_004(self): """ Test bestFit() behavior for non-empty items dictionary with zero-sized items, zero capacity. """ items = buildItemDict(ITEMS_03) capacity = 0 result = bestFit(items, capacity) self.assertEqual(([], 0), result) def testBestFit_005(self): """ Test bestFit() behavior for items dictionary where only one item fits. """ items = buildItemDict(ITEMS_05) capacity = 1 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(1, result[1]) items = buildItemDict(ITEMS_06) capacity = 10 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(10, result[1]) items = buildItemDict(ITEMS_07) capacity = 100 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(100, result[1]) items = buildItemDict(ITEMS_08) capacity = 1000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(1000, result[1]) items = buildItemDict(ITEMS_09) capacity = 10000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(10000, result[1]) items = buildItemDict(ITEMS_10) capacity = 100000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(100000, result[1]) def testBestFit_006(self): """ Test bestFit() behavior for items dictionary where only 25% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 2 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2, result[1]) items = buildItemDict(ITEMS_06) capacity = 25 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20, result[1]) items = buildItemDict(ITEMS_07) capacity = 250 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200, result[1]) items = buildItemDict(ITEMS_08) capacity = 2500 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2000, result[1]) items = buildItemDict(ITEMS_09) capacity = 25000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20000, result[1]) items = buildItemDict(ITEMS_10) capacity = 250000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200000, result[1]) items = buildItemDict(ITEMS_11) capacity = 2 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2, result[1]) items = buildItemDict(ITEMS_12) capacity = 25 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20, result[1]) items = buildItemDict(ITEMS_13) capacity = 250 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200, result[1]) items = buildItemDict(ITEMS_14) capacity = 2500 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2000, result[1]) def testBestFit_007(self): """ Test bestFit() behavior for items dictionary where only 50% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 4 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_06) capacity = 45 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_07) capacity = 450 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_08) capacity = 4500 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) items = buildItemDict(ITEMS_09) capacity = 45000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40000, result[1]) items = buildItemDict(ITEMS_10) capacity = 450000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400000, result[1]) items = buildItemDict(ITEMS_11) capacity = 4 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_12) capacity = 45 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_13) capacity = 450 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_14) capacity = 4500 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) def testBestFit_008(self): """ Test bestFit() behavior for items dictionary where only 75% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 6 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6, result[1]) items = buildItemDict(ITEMS_06) capacity = 65 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60, result[1]) items = buildItemDict(ITEMS_07) capacity = 650 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600, result[1]) items = buildItemDict(ITEMS_08) capacity = 6500 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6000, result[1]) items = buildItemDict(ITEMS_09) capacity = 65000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60000, result[1]) items = buildItemDict(ITEMS_10) capacity = 650000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600000, result[1]) items = buildItemDict(ITEMS_15) capacity = 7 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6, result[1]) items = buildItemDict(ITEMS_16) capacity = 65 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60, result[1]) items = buildItemDict(ITEMS_17) capacity = 650 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600, result[1]) items = buildItemDict(ITEMS_18) capacity = 6500 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6000, result[1]) def testBestFit_009(self): """ Test bestFit() behavior for items dictionary where all items individually exceed the capacity. """ items = buildItemDict(ITEMS_06) capacity = 9 result = bestFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_07) capacity = 99 result = bestFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_08) capacity = 999 result = bestFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_09) capacity = 9999 result = bestFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_10) capacity = 99999 result = bestFit(items, capacity) self.assertEqual(([], 0), result) def testBestFit_010(self): """ Test bestFit() behavior for items dictionary where first half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_04) capacity = 200 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(111, result[1]) def testBestFit_011(self): """ Test bestFit() behavior for items dictionary where middle half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_11) capacity = 5 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_12) capacity = 50 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_13) capacity = 500 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_14) capacity = 5000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) def testBestFit_012(self): """ Test bestFit() behavior for items dictionary where second half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_03) capacity = 200 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(111, result[1]) def testBestFit_013(self): """ Test bestFit() behavior for items dictionary where first half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_04) capacity = 50 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testBestFit_014(self): """ Test bestFit() behavior for items dictionary where middle half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_11) capacity = 3 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_12) capacity = 35 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_13) capacity = 350 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_14) capacity = 3500 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testBestFit_015(self): """ Test bestFit() behavior for items dictionary where second half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_03) capacity = 50 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testBestFit_016(self): """ Test bestFit() behavior for items dictionary where all items fit. """ items = buildItemDict(ITEMS_02) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(0, result[1]) items = buildItemDict(ITEMS_03) capacity = 2000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(1111111, result[1]) items = buildItemDict(ITEMS_04) capacity = 2000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(1111111, result[1]) items = buildItemDict(ITEMS_05) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(8, result[1]) items = buildItemDict(ITEMS_06) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(80, result[1]) items = buildItemDict(ITEMS_07) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(800, result[1]) items = buildItemDict(ITEMS_08) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(8000, result[1]) items = buildItemDict(ITEMS_09) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(80000, result[1]) items = buildItemDict(ITEMS_10) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(800000, result[1]) items = buildItemDict(ITEMS_11) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400004, result[1]) items = buildItemDict(ITEMS_12) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400040, result[1]) items = buildItemDict(ITEMS_13) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400400, result[1]) items = buildItemDict(ITEMS_14) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(404000, result[1]) items = buildItemDict(ITEMS_15) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200006, result[1]) items = buildItemDict(ITEMS_16) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200060, result[1]) items = buildItemDict(ITEMS_17) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200600, result[1]) items = buildItemDict(ITEMS_18) capacity = 1000000 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(206000, result[1]) def testBestFit_017(self): """ Test bestFit() behavior for a more realistic set of items """ items = buildItemDict(ITEMS_19) capacity = 760 result = bestFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(5, len(result[0])) self.assertEqual(753, result[1]) self.assertTrue("dir001/file001" in result[0]) self.assertTrue("dir001/file002" in result[0]) self.assertTrue("file002" in result[0]) self.assertTrue("link001" in result[0]) self.assertTrue("link002" in result[0]) ################################ # Tests for worstFit() function ################################ def testWorstFit_001(self): """ Test worstFit() behavior for an empty items dictionary, zero capacity. """ items = buildItemDict(ITEMS_01) capacity = 0 result = worstFit(items, capacity) self.assertEqual(([], 0), result) def testWorstFit_002(self): """ Test worstFit() behavior for an empty items dictionary, non-zero capacity. """ items = buildItemDict(ITEMS_01) capacity = 10000 result = worstFit(items, capacity) self.assertEqual(([], 0), result) def testWorstFit_003(self): """ Test worstFit() behavior for an non-empty items dictionary, zero capacity. """ items = buildItemDict(ITEMS_03) capacity = 0 result = worstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_04) capacity = 0 result = worstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_13) capacity = 0 result = worstFit(items, capacity) self.assertEqual(([], 0), result) def testWorstFit_004(self): """ Test worstFit() behavior for non-empty items dictionary with zero-sized items, zero capacity. """ items = buildItemDict(ITEMS_03) capacity = 0 result = worstFit(items, capacity) self.assertEqual(([], 0), result) def testWorstFit_005(self): """ Test worstFit() behavior for items dictionary where only one item fits. """ items = buildItemDict(ITEMS_05) capacity = 1 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(1, result[1]) items = buildItemDict(ITEMS_06) capacity = 10 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(10, result[1]) items = buildItemDict(ITEMS_07) capacity = 100 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(100, result[1]) items = buildItemDict(ITEMS_08) capacity = 1000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(1000, result[1]) items = buildItemDict(ITEMS_09) capacity = 10000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(10000, result[1]) items = buildItemDict(ITEMS_10) capacity = 100000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(100000, result[1]) def testWorstFit_006(self): """ Test worstFit() behavior for items dictionary where only 25% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 2 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2, result[1]) items = buildItemDict(ITEMS_06) capacity = 25 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20, result[1]) items = buildItemDict(ITEMS_07) capacity = 250 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200, result[1]) items = buildItemDict(ITEMS_08) capacity = 2500 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2000, result[1]) items = buildItemDict(ITEMS_09) capacity = 25000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20000, result[1]) items = buildItemDict(ITEMS_10) capacity = 250000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200000, result[1]) items = buildItemDict(ITEMS_11) capacity = 2 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2, result[1]) items = buildItemDict(ITEMS_12) capacity = 25 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20, result[1]) items = buildItemDict(ITEMS_13) capacity = 250 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200, result[1]) items = buildItemDict(ITEMS_14) capacity = 2500 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2000, result[1]) def testWorstFit_007(self): """ Test worstFit() behavior for items dictionary where only 50% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 4 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_06) capacity = 45 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_07) capacity = 450 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_08) capacity = 4500 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) items = buildItemDict(ITEMS_09) capacity = 45000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40000, result[1]) items = buildItemDict(ITEMS_10) capacity = 450000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400000, result[1]) items = buildItemDict(ITEMS_11) capacity = 4 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_12) capacity = 45 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_13) capacity = 450 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_14) capacity = 4500 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) def testWorstFit_008(self): """ Test worstFit() behavior for items dictionary where only 75% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 6 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6, result[1]) items = buildItemDict(ITEMS_06) capacity = 65 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60, result[1]) items = buildItemDict(ITEMS_07) capacity = 650 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600, result[1]) items = buildItemDict(ITEMS_08) capacity = 6500 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6000, result[1]) items = buildItemDict(ITEMS_09) capacity = 65000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60000, result[1]) items = buildItemDict(ITEMS_10) capacity = 650000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600000, result[1]) items = buildItemDict(ITEMS_15) capacity = 7 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6, result[1]) items = buildItemDict(ITEMS_16) capacity = 65 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60, result[1]) items = buildItemDict(ITEMS_17) capacity = 650 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600, result[1]) items = buildItemDict(ITEMS_18) capacity = 6500 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6000, result[1]) def testWorstFit_009(self): """ Test worstFit() behavior for items dictionary where all items individually exceed the capacity. """ items = buildItemDict(ITEMS_06) capacity = 9 result = worstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_07) capacity = 99 result = worstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_08) capacity = 999 result = worstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_09) capacity = 9999 result = worstFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_10) capacity = 99999 result = worstFit(items, capacity) self.assertEqual(([], 0), result) def testWorstFit_010(self): """ Test worstFit() behavior for items dictionary where first half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_04) capacity = 200 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(111, result[1]) def testWorstFit_011(self): """ Test worstFit() behavior for items dictionary where middle half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_11) capacity = 5 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_12) capacity = 50 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_13) capacity = 500 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_14) capacity = 5000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) def testWorstFit_012(self): """ Test worstFit() behavior for items dictionary where second half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_03) capacity = 200 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(111, result[1]) def testWorstFit_013(self): """ Test worstFit() behavior for items dictionary where first half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_04) capacity = 50 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testWorstFit_014(self): """ Test worstFit() behavior for items dictionary where middle half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_11) capacity = 3 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_12) capacity = 35 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_13) capacity = 350 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_14) capacity = 3500 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testWorstFit_015(self): """ Test worstFit() behavior for items dictionary where second half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_03) capacity = 50 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testWorstFit_016(self): """ Test worstFit() behavior for items dictionary where all items fit. """ items = buildItemDict(ITEMS_02) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(0, result[1]) items = buildItemDict(ITEMS_03) capacity = 2000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(1111111, result[1]) items = buildItemDict(ITEMS_04) capacity = 2000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(1111111, result[1]) items = buildItemDict(ITEMS_05) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(8, result[1]) items = buildItemDict(ITEMS_06) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(80, result[1]) items = buildItemDict(ITEMS_07) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(800, result[1]) items = buildItemDict(ITEMS_08) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(8000, result[1]) items = buildItemDict(ITEMS_09) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(80000, result[1]) items = buildItemDict(ITEMS_10) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(800000, result[1]) items = buildItemDict(ITEMS_11) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400004, result[1]) items = buildItemDict(ITEMS_12) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400040, result[1]) items = buildItemDict(ITEMS_13) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400400, result[1]) items = buildItemDict(ITEMS_14) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(404000, result[1]) items = buildItemDict(ITEMS_15) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200006, result[1]) items = buildItemDict(ITEMS_16) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200060, result[1]) items = buildItemDict(ITEMS_17) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200600, result[1]) items = buildItemDict(ITEMS_18) capacity = 1000000 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(206000, result[1]) def testWorstFit_017(self): """ Test worstFit() behavior for a more realistic set of items """ items = buildItemDict(ITEMS_19) capacity = 760 result = worstFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(605, result[1]) self.assertTrue("dir002/file001" in result[0]) self.assertTrue("dir002/file002" in result[0]) self.assertTrue("file001" in result[0]) self.assertTrue("file002" in result[0]) self.assertTrue("link001" in result[0]) self.assertTrue("link002" in result[0]) #################################### # Tests for alternateFit() function #################################### def testAlternateFit_001(self): """ Test alternateFit() behavior for an empty items dictionary, zero capacity. """ items = buildItemDict(ITEMS_01) capacity = 0 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) def testAlternateFit_002(self): """ Test alternateFit() behavior for an empty items dictionary, non-zero capacity. """ items = buildItemDict(ITEMS_01) capacity = 10000 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) def testAlternateFit_003(self): """ Test alternateFit() behavior for an non-empty items dictionary, zero capacity. """ items = buildItemDict(ITEMS_03) capacity = 0 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_04) capacity = 0 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_13) capacity = 0 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) def testAlternateFit_004(self): """ Test alternateFit() behavior for non-empty items dictionary with zero-sized items, zero capacity. """ items = buildItemDict(ITEMS_03) capacity = 0 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) def testAlternateFit_005(self): """ Test alternateFit() behavior for items dictionary where only one item fits. """ items = buildItemDict(ITEMS_05) capacity = 1 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(1, result[1]) items = buildItemDict(ITEMS_06) capacity = 10 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(10, result[1]) items = buildItemDict(ITEMS_07) capacity = 100 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(100, result[1]) items = buildItemDict(ITEMS_08) capacity = 1000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(1000, result[1]) items = buildItemDict(ITEMS_09) capacity = 10000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(10000, result[1]) items = buildItemDict(ITEMS_10) capacity = 100000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(1, len(result[0])) self.assertEqual(100000, result[1]) def testAlternateFit_006(self): """ Test alternateFit() behavior for items dictionary where only 25% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 2 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2, result[1]) items = buildItemDict(ITEMS_06) capacity = 25 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20, result[1]) items = buildItemDict(ITEMS_07) capacity = 250 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200, result[1]) items = buildItemDict(ITEMS_08) capacity = 2500 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2000, result[1]) items = buildItemDict(ITEMS_09) capacity = 25000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20000, result[1]) items = buildItemDict(ITEMS_10) capacity = 250000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200000, result[1]) items = buildItemDict(ITEMS_11) capacity = 2 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2, result[1]) items = buildItemDict(ITEMS_12) capacity = 25 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(20, result[1]) items = buildItemDict(ITEMS_13) capacity = 250 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(200, result[1]) items = buildItemDict(ITEMS_14) capacity = 2500 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(2, len(result[0])) self.assertEqual(2000, result[1]) def testAlternateFit_007(self): """ Test alternateFit() behavior for items dictionary where only 50% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 4 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_06) capacity = 45 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_07) capacity = 450 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_08) capacity = 4500 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) items = buildItemDict(ITEMS_09) capacity = 45000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40000, result[1]) items = buildItemDict(ITEMS_10) capacity = 450000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400000, result[1]) items = buildItemDict(ITEMS_11) capacity = 4 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_12) capacity = 45 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_13) capacity = 450 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_14) capacity = 4500 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) def testAlternateFit_008(self): """ Test alternateFit() behavior for items dictionary where only 75% of items fit. """ items = buildItemDict(ITEMS_05) capacity = 6 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6, result[1]) items = buildItemDict(ITEMS_06) capacity = 65 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60, result[1]) items = buildItemDict(ITEMS_07) capacity = 650 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600, result[1]) items = buildItemDict(ITEMS_08) capacity = 6500 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6000, result[1]) items = buildItemDict(ITEMS_09) capacity = 65000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60000, result[1]) items = buildItemDict(ITEMS_10) capacity = 650000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600000, result[1]) items = buildItemDict(ITEMS_15) capacity = 7 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6, result[1]) items = buildItemDict(ITEMS_16) capacity = 65 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(60, result[1]) items = buildItemDict(ITEMS_17) capacity = 650 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(600, result[1]) items = buildItemDict(ITEMS_18) capacity = 6500 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(6000, result[1]) def testAlternateFit_009(self): """ Test alternateFit() behavior for items dictionary where all items individually exceed the capacity. """ items = buildItemDict(ITEMS_06) capacity = 9 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_07) capacity = 99 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_08) capacity = 999 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_09) capacity = 9999 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) items = buildItemDict(ITEMS_10) capacity = 99999 result = alternateFit(items, capacity) self.assertEqual(([], 0), result) def testAlternateFit_010(self): """ Test alternateFit() behavior for items dictionary where first half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_04) capacity = 200 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(111, result[1]) def testAlternateFit_011(self): """ Test alternateFit() behavior for items dictionary where middle half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_11) capacity = 5 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4, result[1]) items = buildItemDict(ITEMS_12) capacity = 50 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(40, result[1]) items = buildItemDict(ITEMS_13) capacity = 500 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(400, result[1]) items = buildItemDict(ITEMS_14) capacity = 5000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(4000, result[1]) def testAlternateFit_012(self): """ Test alternateFit() behavior for items dictionary where second half of items individually exceed capacity and remainder fit. """ items = buildItemDict(ITEMS_03) capacity = 200 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(4, len(result[0])) self.assertEqual(111, result[1]) def testAlternateFit_013(self): """ Test alternateFit() behavior for items dictionary where first half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_04) capacity = 50 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testAlternateFit_014(self): """ Test alternateFit() behavior for items dictionary where middle half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_11) capacity = 3 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_12) capacity = 35 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_13) capacity = 350 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) items = buildItemDict(ITEMS_14) capacity = 3500 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testAlternateFit_015(self): """ Test alternateFit() behavior for items dictionary where second half of items individually exceed capacity and only some of remainder fit. """ items = buildItemDict(ITEMS_03) capacity = 50 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertTrue(len(result[0]) < 4, "%s < 4" % len(result[0])) def testAlternateFit_016(self): """ Test alternateFit() behavior for items dictionary where all items fit. """ items = buildItemDict(ITEMS_02) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(0, result[1]) items = buildItemDict(ITEMS_03) capacity = 2000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(1111111, result[1]) items = buildItemDict(ITEMS_04) capacity = 2000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(1111111, result[1]) items = buildItemDict(ITEMS_05) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(8, result[1]) items = buildItemDict(ITEMS_06) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(80, result[1]) items = buildItemDict(ITEMS_07) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(800, result[1]) items = buildItemDict(ITEMS_08) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(8000, result[1]) items = buildItemDict(ITEMS_09) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(80000, result[1]) items = buildItemDict(ITEMS_10) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(800000, result[1]) items = buildItemDict(ITEMS_11) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400004, result[1]) items = buildItemDict(ITEMS_12) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400040, result[1]) items = buildItemDict(ITEMS_13) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(400400, result[1]) items = buildItemDict(ITEMS_14) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(404000, result[1]) items = buildItemDict(ITEMS_15) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200006, result[1]) items = buildItemDict(ITEMS_16) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200060, result[1]) items = buildItemDict(ITEMS_17) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(200600, result[1]) items = buildItemDict(ITEMS_18) capacity = 1000000 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(8, len(result[0])) self.assertEqual(206000, result[1]) def testAlternateFit_017(self): """ Test alternateFit() behavior for a more realistic set of items """ items = buildItemDict(ITEMS_19) capacity = 760 result = alternateFit(items, capacity) self.assertTrue(result[1] <= capacity, "%s <= %s" % (result[1], capacity)) self.assertEqual(6, len(result[0])) self.assertEqual(719, result[1]) self.assertTrue("link001" in result[0]) self.assertTrue("dir001/file002" in result[0]) self.assertTrue("link002" in result[0]) self.assertTrue("dir001/file001" in result[0]) self.assertTrue("dir002/file002" in result[0]) self.assertTrue("dir002/file001" in result[0]) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1609564 cedar_backup3-3.8.1/tests/test_mbox.py0000644000000000000000000024524314567004737014706 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2006,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests mbox extension functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/extend/mbox.py. Code Coverage ============= This module contains individual tests for the many of the public functions and classes implemented in extend/mbox.py. There are also tests for several of the private methods. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract a node, build some XML from it, and then feed that XML back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a MBOXTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.extend.mbox import LocalConfig, MboxConfig, MboxDir, MboxFile from CedarBackup3.testutil import configureLogging, failUnlessAssignRaises, findResources from CedarBackup3.xmlutil import createOutputDom, serializeDom ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "mbox.conf.1", "mbox.conf.2", "mbox.conf.3", "mbox.conf.4", ] ####################################################################### # Test Case Classes ####################################################################### ##################### # TestMboxFile class ##################### class TestMboxFile(unittest.TestCase): """Tests for the MboxFile class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = MboxFile() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.absolutePath) self.assertEqual(None, mboxFile.collectMode) self.assertEqual(None, mboxFile.compressMode) def testConstructor_002(self): """ Test constructor with all values filled in. """ mboxFile = MboxFile("/path/to/it", "daily", "gzip") self.assertEqual("/path/to/it", mboxFile.absolutePath) self.assertEqual("daily", mboxFile.collectMode) self.assertEqual("gzip", mboxFile.compressMode) def testConstructor_003(self): """ Test assignment of absolutePath attribute, None value. """ mboxFile = MboxFile(absolutePath="/path/to/something") self.assertEqual("/path/to/something", mboxFile.absolutePath) mboxFile.absolutePath = None self.assertEqual(None, mboxFile.absolutePath) def testConstructor_004(self): """ Test assignment of absolutePath attribute, valid value. """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.absolutePath) mboxFile.absolutePath = "/path/to/whatever" self.assertEqual("/path/to/whatever", mboxFile.absolutePath) def testConstructor_005(self): """ Test assignment of absolutePath attribute, invalid value (empty). """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.absolutePath) self.failUnlessAssignRaises(ValueError, mboxFile, "absolutePath", "") self.assertEqual(None, mboxFile.absolutePath) def testConstructor_006(self): """ Test assignment of absolutePath attribute, invalid value (not absolute). """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.absolutePath) self.failUnlessAssignRaises(ValueError, mboxFile, "absolutePath", "relative/path") self.assertEqual(None, mboxFile.absolutePath) def testConstructor_007(self): """ Test assignment of collectMode attribute, None value. """ mboxFile = MboxFile(collectMode="daily") self.assertEqual("daily", mboxFile.collectMode) mboxFile.collectMode = None self.assertEqual(None, mboxFile.collectMode) def testConstructor_008(self): """ Test assignment of collectMode attribute, valid value. """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.collectMode) mboxFile.collectMode = "daily" self.assertEqual("daily", mboxFile.collectMode) mboxFile.collectMode = "weekly" self.assertEqual("weekly", mboxFile.collectMode) mboxFile.collectMode = "incr" self.assertEqual("incr", mboxFile.collectMode) def testConstructor_009(self): """ Test assignment of collectMode attribute, invalid value (empty). """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.collectMode) self.failUnlessAssignRaises(ValueError, mboxFile, "collectMode", "") self.assertEqual(None, mboxFile.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.collectMode) self.failUnlessAssignRaises(ValueError, mboxFile, "collectMode", "monthly") self.assertEqual(None, mboxFile.collectMode) def testConstructor_011(self): """ Test assignment of compressMode attribute, None value. """ mboxFile = MboxFile(compressMode="gzip") self.assertEqual("gzip", mboxFile.compressMode) mboxFile.compressMode = None self.assertEqual(None, mboxFile.compressMode) def testConstructor_012(self): """ Test assignment of compressMode attribute, valid value. """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.compressMode) mboxFile.compressMode = "none" self.assertEqual("none", mboxFile.compressMode) mboxFile.compressMode = "bzip2" self.assertEqual("bzip2", mboxFile.compressMode) mboxFile.compressMode = "gzip" self.assertEqual("gzip", mboxFile.compressMode) def testConstructor_013(self): """ Test assignment of compressMode attribute, invalid value (empty). """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.compressMode) self.failUnlessAssignRaises(ValueError, mboxFile, "compressMode", "") self.assertEqual(None, mboxFile.compressMode) def testConstructor_014(self): """ Test assignment of compressMode attribute, invalid value (not in list). """ mboxFile = MboxFile() self.assertEqual(None, mboxFile.compressMode) self.failUnlessAssignRaises(ValueError, mboxFile, "compressMode", "compress") self.assertEqual(None, mboxFile.compressMode) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ mboxFile1 = MboxFile() mboxFile2 = MboxFile() self.assertEqual(mboxFile1, mboxFile2) self.assertTrue(mboxFile1 == mboxFile2) self.assertTrue(not mboxFile1 < mboxFile2) self.assertTrue(mboxFile1 <= mboxFile2) self.assertTrue(not mboxFile1 > mboxFile2) self.assertTrue(mboxFile1 >= mboxFile2) self.assertTrue(not mboxFile1 != mboxFile2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ mboxFile1 = MboxFile("/path", "daily", "gzip") mboxFile2 = MboxFile("/path", "daily", "gzip") self.assertEqual(mboxFile1, mboxFile2) self.assertTrue(mboxFile1 == mboxFile2) self.assertTrue(not mboxFile1 < mboxFile2) self.assertTrue(mboxFile1 <= mboxFile2) self.assertTrue(not mboxFile1 > mboxFile2) self.assertTrue(mboxFile1 >= mboxFile2) self.assertTrue(not mboxFile1 != mboxFile2) def testComparison_003(self): """ Test comparison of two differing objects, absolutePath differs (one None). """ mboxFile1 = MboxFile() mboxFile2 = MboxFile(absolutePath="/zippy") self.assertNotEqual(mboxFile1, mboxFile2) self.assertTrue(not mboxFile1 == mboxFile2) self.assertTrue(mboxFile1 < mboxFile2) self.assertTrue(mboxFile1 <= mboxFile2) self.assertTrue(not mboxFile1 > mboxFile2) self.assertTrue(not mboxFile1 >= mboxFile2) self.assertTrue(mboxFile1 != mboxFile2) def testComparison_004(self): """ Test comparison of two differing objects, absolutePath differs. """ mboxFile1 = MboxFile("/path", "daily", "gzip") mboxFile2 = MboxFile("/zippy", "daily", "gzip") self.assertNotEqual(mboxFile1, mboxFile2) self.assertTrue(not mboxFile1 == mboxFile2) self.assertTrue(mboxFile1 < mboxFile2) self.assertTrue(mboxFile1 <= mboxFile2) self.assertTrue(not mboxFile1 > mboxFile2) self.assertTrue(not mboxFile1 >= mboxFile2) self.assertTrue(mboxFile1 != mboxFile2) def testComparison_005(self): """ Test comparison of two differing objects, collectMode differs (one None). """ mboxFile1 = MboxFile() mboxFile2 = MboxFile(collectMode="incr") self.assertNotEqual(mboxFile1, mboxFile2) self.assertTrue(not mboxFile1 == mboxFile2) self.assertTrue(mboxFile1 < mboxFile2) self.assertTrue(mboxFile1 <= mboxFile2) self.assertTrue(not mboxFile1 > mboxFile2) self.assertTrue(not mboxFile1 >= mboxFile2) self.assertTrue(mboxFile1 != mboxFile2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs. """ mboxFile1 = MboxFile("/path", "daily", "gzip") mboxFile2 = MboxFile("/path", "incr", "gzip") self.assertNotEqual(mboxFile1, mboxFile2) self.assertTrue(not mboxFile1 == mboxFile2) self.assertTrue(mboxFile1 < mboxFile2) self.assertTrue(mboxFile1 <= mboxFile2) self.assertTrue(not mboxFile1 > mboxFile2) self.assertTrue(not mboxFile1 >= mboxFile2) self.assertTrue(mboxFile1 != mboxFile2) def testComparison_007(self): """ Test comparison of two differing objects, compressMode differs (one None). """ mboxFile1 = MboxFile() mboxFile2 = MboxFile(compressMode="gzip") self.assertNotEqual(mboxFile1, mboxFile2) self.assertTrue(not mboxFile1 == mboxFile2) self.assertTrue(mboxFile1 < mboxFile2) self.assertTrue(mboxFile1 <= mboxFile2) self.assertTrue(not mboxFile1 > mboxFile2) self.assertTrue(not mboxFile1 >= mboxFile2) self.assertTrue(mboxFile1 != mboxFile2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs. """ mboxFile1 = MboxFile("/path", "daily", "bzip2") mboxFile2 = MboxFile("/path", "daily", "gzip") self.assertNotEqual(mboxFile1, mboxFile2) self.assertTrue(not mboxFile1 == mboxFile2) self.assertTrue(mboxFile1 < mboxFile2) self.assertTrue(mboxFile1 <= mboxFile2) self.assertTrue(not mboxFile1 > mboxFile2) self.assertTrue(not mboxFile1 >= mboxFile2) self.assertTrue(mboxFile1 != mboxFile2) ##################### # TestMboxDir class ##################### class TestMboxDir(unittest.TestCase): """Tests for the MboxDir class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = MboxDir() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.absolutePath) self.assertEqual(None, mboxDir.collectMode) self.assertEqual(None, mboxDir.compressMode) self.assertEqual(None, mboxDir.relativeExcludePaths) self.assertEqual(None, mboxDir.excludePatterns) def testConstructor_002(self): """ Test constructor with all values filled in. """ mboxDir = MboxDir("/path/to/it", "daily", "gzip", ["whatever"], [".*SPAM.*"]) self.assertEqual("/path/to/it", mboxDir.absolutePath) self.assertEqual("daily", mboxDir.collectMode) self.assertEqual("gzip", mboxDir.compressMode) self.assertEqual(["whatever"], mboxDir.relativeExcludePaths) self.assertEqual([".*SPAM.*"], mboxDir.excludePatterns) def testConstructor_003(self): """ Test assignment of absolutePath attribute, None value. """ mboxDir = MboxDir(absolutePath="/path/to/something") self.assertEqual("/path/to/something", mboxDir.absolutePath) mboxDir.absolutePath = None self.assertEqual(None, mboxDir.absolutePath) def testConstructor_004(self): """ Test assignment of absolutePath attribute, valid value. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.absolutePath) mboxDir.absolutePath = "/path/to/whatever" self.assertEqual("/path/to/whatever", mboxDir.absolutePath) def testConstructor_005(self): """ Test assignment of absolutePath attribute, invalid value (empty). """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.absolutePath) self.failUnlessAssignRaises(ValueError, mboxDir, "absolutePath", "") self.assertEqual(None, mboxDir.absolutePath) def testConstructor_006(self): """ Test assignment of absolutePath attribute, invalid value (not absolute). """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.absolutePath) self.failUnlessAssignRaises(ValueError, mboxDir, "absolutePath", "relative/path") self.assertEqual(None, mboxDir.absolutePath) def testConstructor_007(self): """ Test assignment of collectMode attribute, None value. """ mboxDir = MboxDir(collectMode="daily") self.assertEqual("daily", mboxDir.collectMode) mboxDir.collectMode = None self.assertEqual(None, mboxDir.collectMode) def testConstructor_008(self): """ Test assignment of collectMode attribute, valid value. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.collectMode) mboxDir.collectMode = "daily" self.assertEqual("daily", mboxDir.collectMode) mboxDir.collectMode = "weekly" self.assertEqual("weekly", mboxDir.collectMode) mboxDir.collectMode = "incr" self.assertEqual("incr", mboxDir.collectMode) def testConstructor_009(self): """ Test assignment of collectMode attribute, invalid value (empty). """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.collectMode) self.failUnlessAssignRaises(ValueError, mboxDir, "collectMode", "") self.assertEqual(None, mboxDir.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.collectMode) self.failUnlessAssignRaises(ValueError, mboxDir, "collectMode", "monthly") self.assertEqual(None, mboxDir.collectMode) def testConstructor_011(self): """ Test assignment of compressMode attribute, None value. """ mboxDir = MboxDir(compressMode="gzip") self.assertEqual("gzip", mboxDir.compressMode) mboxDir.compressMode = None self.assertEqual(None, mboxDir.compressMode) def testConstructor_012(self): """ Test assignment of compressMode attribute, valid value. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.compressMode) mboxDir.compressMode = "none" self.assertEqual("none", mboxDir.compressMode) mboxDir.compressMode = "bzip2" self.assertEqual("bzip2", mboxDir.compressMode) mboxDir.compressMode = "gzip" self.assertEqual("gzip", mboxDir.compressMode) def testConstructor_013(self): """ Test assignment of compressMode attribute, invalid value (empty). """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.compressMode) self.failUnlessAssignRaises(ValueError, mboxDir, "compressMode", "") self.assertEqual(None, mboxDir.compressMode) def testConstructor_014(self): """ Test assignment of compressMode attribute, invalid value (not in list). """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.compressMode) self.failUnlessAssignRaises(ValueError, mboxDir, "compressMode", "compress") self.assertEqual(None, mboxDir.compressMode) def testConstructor_015(self): """ Test assignment of relativeExcludePaths attribute, None value. """ mboxDir = MboxDir(relativeExcludePaths=[]) self.assertEqual([], mboxDir.relativeExcludePaths) mboxDir.relativeExcludePaths = None self.assertEqual(None, mboxDir.relativeExcludePaths) def testConstructor_016(self): """ Test assignment of relativeExcludePaths attribute, [] value. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.relativeExcludePaths) mboxDir.relativeExcludePaths = [] self.assertEqual([], mboxDir.relativeExcludePaths) def testConstructor_017(self): """ Test assignment of relativeExcludePaths attribute, single valid entry. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.relativeExcludePaths) mboxDir.relativeExcludePaths = [ "stuff", ] self.assertEqual(["stuff"], mboxDir.relativeExcludePaths) mboxDir.relativeExcludePaths.insert(0, "bogus") self.assertEqual(["bogus", "stuff"], mboxDir.relativeExcludePaths) def testConstructor_018(self): """ Test assignment of relativeExcludePaths attribute, multiple valid entries. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.relativeExcludePaths) mboxDir.relativeExcludePaths = [ "bogus", "stuff", ] self.assertEqual(["bogus", "stuff"], mboxDir.relativeExcludePaths) mboxDir.relativeExcludePaths.append("more") self.assertEqual(["bogus", "stuff", "more"], mboxDir.relativeExcludePaths) def testConstructor_019(self): """ Test assignment of excludePatterns attribute, None value. """ mboxDir = MboxDir(excludePatterns=[]) self.assertEqual([], mboxDir.excludePatterns) mboxDir.excludePatterns = None self.assertEqual(None, mboxDir.excludePatterns) def testConstructor_020(self): """ Test assignment of excludePatterns attribute, [] value. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.excludePatterns) mboxDir.excludePatterns = [] self.assertEqual([], mboxDir.excludePatterns) def testConstructor_021(self): """ Test assignment of excludePatterns attribute, single valid entry. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.excludePatterns) mboxDir.excludePatterns = [ "valid", ] self.assertEqual(["valid"], mboxDir.excludePatterns) mboxDir.excludePatterns.append("more") self.assertEqual(["valid", "more"], mboxDir.excludePatterns) def testConstructor_022(self): """ Test assignment of excludePatterns attribute, multiple valid entries. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.excludePatterns) mboxDir.excludePatterns = [ "valid", "more", ] self.assertEqual(["valid", "more"], mboxDir.excludePatterns) mboxDir.excludePatterns.insert(1, "bogus") self.assertEqual(["valid", "bogus", "more"], mboxDir.excludePatterns) def testConstructor_023(self): """ Test assignment of excludePatterns attribute, single invalid entry. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.excludePatterns) self.failUnlessAssignRaises(ValueError, mboxDir, "excludePatterns", ["*.jpg"]) self.assertEqual(None, mboxDir.excludePatterns) def testConstructor_024(self): """ Test assignment of excludePatterns attribute, multiple invalid entries. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.excludePatterns) self.failUnlessAssignRaises(ValueError, mboxDir, "excludePatterns", ["*.jpg", "*"]) self.assertEqual(None, mboxDir.excludePatterns) def testConstructor_025(self): """ Test assignment of excludePatterns attribute, mixed valid and invalid entries. """ mboxDir = MboxDir() self.assertEqual(None, mboxDir.excludePatterns) self.failUnlessAssignRaises(ValueError, mboxDir, "excludePatterns", ["*.jpg", "valid"]) self.assertEqual(None, mboxDir.excludePatterns) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ mboxDir1 = MboxDir() mboxDir2 = MboxDir() self.assertEqual(mboxDir1, mboxDir2) self.assertTrue(mboxDir1 == mboxDir2) self.assertTrue(not mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(mboxDir1 >= mboxDir2) self.assertTrue(not mboxDir1 != mboxDir2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ mboxDir1 = MboxDir("/path", "daily", "gzip") mboxDir2 = MboxDir("/path", "daily", "gzip") self.assertEqual(mboxDir1, mboxDir2) self.assertTrue(mboxDir1 == mboxDir2) self.assertTrue(not mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(mboxDir1 >= mboxDir2) self.assertTrue(not mboxDir1 != mboxDir2) def testComparison_003(self): """ Test comparison of two differing objects, absolutePath differs (one None). """ mboxDir1 = MboxDir() mboxDir2 = MboxDir(absolutePath="/zippy") self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_004(self): """ Test comparison of two differing objects, absolutePath differs. """ mboxDir1 = MboxDir("/path", "daily", "gzip") mboxDir2 = MboxDir("/zippy", "daily", "gzip") self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_005(self): """ Test comparison of two differing objects, collectMode differs (one None). """ mboxDir1 = MboxDir() mboxDir2 = MboxDir(collectMode="incr") self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs. """ mboxDir1 = MboxDir("/path", "daily", "gzip") mboxDir2 = MboxDir("/path", "incr", "gzip") self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_007(self): """ Test comparison of two differing objects, compressMode differs (one None). """ mboxDir1 = MboxDir() mboxDir2 = MboxDir(compressMode="gzip") self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs. """ mboxDir1 = MboxDir("/path", "daily", "bzip2") mboxDir2 = MboxDir("/path", "daily", "gzip") self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_009(self): """ Test comparison of two differing objects, relativeExcludePaths differs (one None, one empty). """ mboxDir1 = MboxDir() mboxDir2 = MboxDir(relativeExcludePaths=[]) self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_010(self): """ Test comparison of two differing objects, relativeExcludePaths differs (one None, one not empty). """ mboxDir1 = MboxDir() mboxDir2 = MboxDir(relativeExcludePaths=["stuff", "other"]) self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_011(self): """ Test comparison of two differing objects, relativeExcludePaths differs (one empty, one not empty). """ mboxDir1 = MboxDir("/etc/whatever", "incr", "none", ["one"], []) mboxDir2 = MboxDir("/etc/whatever", "incr", "none", [], []) self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(not mboxDir1 < mboxDir2) self.assertTrue(not mboxDir1 <= mboxDir2) self.assertTrue(mboxDir1 > mboxDir2) self.assertTrue(mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_012(self): """ Test comparison of two differing objects, relativeExcludePaths differs (both not empty). """ mboxDir1 = MboxDir("/etc/whatever", "incr", "none", ["one"], []) mboxDir2 = MboxDir("/etc/whatever", "incr", "none", ["two"], []) self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_013(self): """ Test comparison of two differing objects, excludePatterns differs (one None, one empty). """ mboxDir1 = MboxDir() mboxDir2 = MboxDir(excludePatterns=[]) self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_014(self): """ Test comparison of two differing objects, excludePatterns differs (one None, one not empty). """ mboxDir1 = MboxDir() mboxDir2 = MboxDir(excludePatterns=["one", "two", "three"]) self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_015(self): """ Test comparison of two differing objects, excludePatterns differs (one empty, one not empty). """ mboxDir1 = MboxDir("/etc/whatever", "incr", "none", [], []) mboxDir2 = MboxDir("/etc/whatever", "incr", "none", [], ["pattern"]) self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) def testComparison_016(self): """ Test comparison of two differing objects, excludePatterns differs (both not empty). """ mboxDir1 = MboxDir("/etc/whatever", "incr", "none", [], ["p1"]) mboxDir2 = MboxDir("/etc/whatever", "incr", "none", [], ["p2"]) self.assertNotEqual(mboxDir1, mboxDir2) self.assertTrue(not mboxDir1 == mboxDir2) self.assertTrue(mboxDir1 < mboxDir2) self.assertTrue(mboxDir1 <= mboxDir2) self.assertTrue(not mboxDir1 > mboxDir2) self.assertTrue(not mboxDir1 >= mboxDir2) self.assertTrue(mboxDir1 != mboxDir2) ####################### # TestMboxConfig class ####################### class TestMboxConfig(unittest.TestCase): """Tests for the MboxConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = MboxConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ mbox = MboxConfig() self.assertEqual(None, mbox.collectMode) self.assertEqual(None, mbox.compressMode) self.assertEqual(None, mbox.mboxFiles) self.assertEqual(None, mbox.mboxDirs) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values, mboxFiles=None and mboxDirs=None. """ mbox = MboxConfig("daily", "gzip", None, None) self.assertEqual("daily", mbox.collectMode) self.assertEqual("gzip", mbox.compressMode) self.assertEqual(None, mbox.mboxFiles) self.assertEqual(None, mbox.mboxDirs) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values, no mboxFiles, no mboxDirs. """ mbox = MboxConfig("daily", "gzip", [], []) self.assertEqual("daily", mbox.collectMode) self.assertEqual("gzip", mbox.compressMode) self.assertEqual([], mbox.mboxFiles) self.assertEqual([], mbox.mboxDirs) def testConstructor_004(self): """ Test constructor with all values filled in, with valid values, with one mboxFile, no mboxDirs. """ mboxFiles = [ MboxFile(), ] mbox = MboxConfig("daily", "gzip", mboxFiles, []) self.assertEqual("daily", mbox.collectMode) self.assertEqual("gzip", mbox.compressMode) self.assertEqual(mboxFiles, mbox.mboxFiles) self.assertEqual([], mbox.mboxDirs) def testConstructor_005(self): """ Test constructor with all values filled in, with valid values, with no mboxFiles, one mboxDir. """ mboxDirs = [ MboxDir(), ] mbox = MboxConfig("daily", "gzip", [], mboxDirs) self.assertEqual("daily", mbox.collectMode) self.assertEqual("gzip", mbox.compressMode) self.assertEqual([], mbox.mboxFiles) self.assertEqual(mboxDirs, mbox.mboxDirs) def testConstructor_006(self): """ Test constructor with all values filled in, with valid values, with multiple mboxFiles and mboxDirs. """ mboxFiles = [ MboxFile(collectMode="daily"), MboxFile(collectMode="weekly"), ] mboxDirs = [ MboxDir(collectMode="weekly"), MboxDir(collectMode="incr"), ] mbox = MboxConfig("daily", "gzip", mboxFiles=mboxFiles, mboxDirs=mboxDirs) self.assertEqual("daily", mbox.collectMode) self.assertEqual("gzip", mbox.compressMode) self.assertEqual(mboxFiles, mbox.mboxFiles) self.assertEqual(mboxDirs, mbox.mboxDirs) def testConstructor_007(self): """ Test assignment of collectMode attribute, None value. """ mbox = MboxConfig(collectMode="daily") self.assertEqual("daily", mbox.collectMode) mbox.collectMode = None self.assertEqual(None, mbox.collectMode) def testConstructor_008(self): """ Test assignment of collectMode attribute, valid value. """ mbox = MboxConfig() self.assertEqual(None, mbox.collectMode) mbox.collectMode = "weekly" self.assertEqual("weekly", mbox.collectMode) def testConstructor_009(self): """ Test assignment of collectMode attribute, invalid value (empty). """ mbox = MboxConfig() self.assertEqual(None, mbox.collectMode) self.failUnlessAssignRaises(ValueError, mbox, "collectMode", "") self.assertEqual(None, mbox.collectMode) def testConstructor_010(self): """ Test assignment of compressMode attribute, None value. """ mbox = MboxConfig(compressMode="gzip") self.assertEqual("gzip", mbox.compressMode) mbox.compressMode = None self.assertEqual(None, mbox.compressMode) def testConstructor_011(self): """ Test assignment of compressMode attribute, valid value. """ mbox = MboxConfig() self.assertEqual(None, mbox.compressMode) mbox.compressMode = "bzip2" self.assertEqual("bzip2", mbox.compressMode) def testConstructor_012(self): """ Test assignment of compressMode attribute, invalid value (empty). """ mbox = MboxConfig() self.assertEqual(None, mbox.compressMode) self.failUnlessAssignRaises(ValueError, mbox, "compressMode", "") self.assertEqual(None, mbox.compressMode) def testConstructor_013(self): """ Test assignment of mboxFiles attribute, None value. """ mbox = MboxConfig(mboxFiles=[]) self.assertEqual([], mbox.mboxFiles) mbox.mboxFiles = None self.assertEqual(None, mbox.mboxFiles) def testConstructor_014(self): """ Test assignment of mboxFiles attribute, [] value. """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxFiles) mbox.mboxFiles = [] self.assertEqual([], mbox.mboxFiles) def testConstructor_015(self): """ Test assignment of mboxFiles attribute, single valid entry. """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxFiles) mbox.mboxFiles = [ MboxFile(), ] self.assertEqual([MboxFile()], mbox.mboxFiles) mbox.mboxFiles.append(MboxFile(collectMode="daily")) self.assertEqual([MboxFile(), MboxFile(collectMode="daily")], mbox.mboxFiles) def testConstructor_016(self): """ Test assignment of mboxFiles attribute, multiple valid entries. """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxFiles) mbox.mboxFiles = [ MboxFile(collectMode="daily"), MboxFile(collectMode="weekly"), ] self.assertEqual([MboxFile(collectMode="daily"), MboxFile(collectMode="weekly")], mbox.mboxFiles) mbox.mboxFiles.append(MboxFile(collectMode="incr")) self.assertEqual( [MboxFile(collectMode="daily"), MboxFile(collectMode="weekly"), MboxFile(collectMode="incr")], mbox.mboxFiles ) def testConstructor_017(self): """ Test assignment of mboxFiles attribute, single invalid entry (None). """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxFiles) self.failUnlessAssignRaises(ValueError, mbox, "mboxFiles", [None]) self.assertEqual(None, mbox.mboxFiles) def testConstructor_018(self): """ Test assignment of mboxFiles attribute, single invalid entry (wrong type). """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxFiles) self.failUnlessAssignRaises(ValueError, mbox, "mboxFiles", [MboxDir()]) self.assertEqual(None, mbox.mboxFiles) def testConstructor_019(self): """ Test assignment of mboxFiles attribute, mixed valid and invalid entries. """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxFiles) self.failUnlessAssignRaises(ValueError, mbox, "mboxFiles", [MboxFile(), MboxDir()]) self.assertEqual(None, mbox.mboxFiles) def testConstructor_020(self): """ Test assignment of mboxDirs attribute, None value. """ mbox = MboxConfig(mboxDirs=[]) self.assertEqual([], mbox.mboxDirs) mbox.mboxDirs = None self.assertEqual(None, mbox.mboxDirs) def testConstructor_021(self): """ Test assignment of mboxDirs attribute, [] value. """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxDirs) mbox.mboxDirs = [] self.assertEqual([], mbox.mboxDirs) def testConstructor_022(self): """ Test assignment of mboxDirs attribute, single valid entry. """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxDirs) mbox.mboxDirs = [ MboxDir(), ] self.assertEqual([MboxDir()], mbox.mboxDirs) mbox.mboxDirs.append(MboxDir(collectMode="daily")) self.assertEqual([MboxDir(), MboxDir(collectMode="daily")], mbox.mboxDirs) def testConstructor_023(self): """ Test assignment of mboxDirs attribute, multiple valid entries. """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxDirs) mbox.mboxDirs = [ MboxDir(collectMode="daily"), MboxDir(collectMode="weekly"), ] self.assertEqual([MboxDir(collectMode="daily"), MboxDir(collectMode="weekly")], mbox.mboxDirs) mbox.mboxDirs.append(MboxDir(collectMode="incr")) self.assertEqual([MboxDir(collectMode="daily"), MboxDir(collectMode="weekly"), MboxDir(collectMode="incr")], mbox.mboxDirs) def testConstructor_024(self): """ Test assignment of mboxDirs attribute, single invalid entry (None). """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxDirs) self.failUnlessAssignRaises(ValueError, mbox, "mboxDirs", [None]) self.assertEqual(None, mbox.mboxDirs) def testConstructor_025(self): """ Test assignment of mboxDirs attribute, single invalid entry (wrong type). """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxDirs) self.failUnlessAssignRaises(ValueError, mbox, "mboxDirs", [MboxFile()]) self.assertEqual(None, mbox.mboxDirs) def testConstructor_026(self): """ Test assignment of mboxDirs attribute, mixed valid and invalid entries. """ mbox = MboxConfig() self.assertEqual(None, mbox.mboxDirs) self.failUnlessAssignRaises(ValueError, mbox, "mboxDirs", [MboxDir(), MboxFile()]) self.assertEqual(None, mbox.mboxDirs) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ mbox1 = MboxConfig() mbox2 = MboxConfig() self.assertEqual(mbox1, mbox2) self.assertTrue(mbox1 == mbox2) self.assertTrue(not mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(mbox1 >= mbox2) self.assertTrue(not mbox1 != mbox2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None, lists None. """ mbox1 = MboxConfig("daily", "gzip", None, None) mbox2 = MboxConfig("daily", "gzip", None, None) self.assertEqual(mbox1, mbox2) self.assertTrue(mbox1 == mbox2) self.assertTrue(not mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(mbox1 >= mbox2) self.assertTrue(not mbox1 != mbox2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None, lists empty. """ mbox1 = MboxConfig("daily", "gzip", [], []) mbox2 = MboxConfig("daily", "gzip", [], []) self.assertEqual(mbox1, mbox2) self.assertTrue(mbox1 == mbox2) self.assertTrue(not mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(mbox1 >= mbox2) self.assertTrue(not mbox1 != mbox2) def testComparison_004(self): """ Test comparison of two identical objects, all attributes non-None, lists non-empty. """ mbox1 = MboxConfig("daily", "gzip", [MboxFile()], [MboxDir()]) mbox2 = MboxConfig("daily", "gzip", [MboxFile()], [MboxDir()]) self.assertEqual(mbox1, mbox2) self.assertTrue(mbox1 == mbox2) self.assertTrue(not mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(mbox1 >= mbox2) self.assertTrue(not mbox1 != mbox2) def testComparison_005(self): """ Test comparison of two differing objects, collectMode differs (one None). """ mbox1 = MboxConfig() mbox2 = MboxConfig(collectMode="daily") self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs. """ mbox1 = MboxConfig("daily", "gzip", [MboxFile()]) mbox2 = MboxConfig("weekly", "gzip", [MboxFile()]) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_007(self): """ Test comparison of two differing objects, compressMode differs (one None). """ mbox1 = MboxConfig() mbox2 = MboxConfig(compressMode="bzip2") self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs. """ mbox1 = MboxConfig("daily", "bzip2", [MboxFile()]) mbox2 = MboxConfig("daily", "gzip", [MboxFile()]) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_009(self): """ Test comparison of two differing objects, mboxFiles differs (one None, one empty). """ mbox1 = MboxConfig() mbox2 = MboxConfig(mboxFiles=[]) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_010(self): """ Test comparison of two differing objects, mboxFiles differs (one None, one not empty). """ mbox1 = MboxConfig() mbox2 = MboxConfig(mboxFiles=[MboxFile()]) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_011(self): """ Test comparison of two differing objects, mboxFiles differs (one empty, one not empty). """ mbox1 = MboxConfig("daily", "gzip", [], None) mbox2 = MboxConfig("daily", "gzip", [MboxFile()], None) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_012(self): """ Test comparison of two differing objects, mboxFiles differs (both not empty). """ mbox1 = MboxConfig("daily", "gzip", [MboxFile()], None) mbox2 = MboxConfig("daily", "gzip", [MboxFile(), MboxFile()], None) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_013(self): """ Test comparison of two differing objects, mboxDirs differs (one None, one empty). """ mbox1 = MboxConfig() mbox2 = MboxConfig(mboxDirs=[]) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_014(self): """ Test comparison of two differing objects, mboxDirs differs (one None, one not empty). """ mbox1 = MboxConfig() mbox2 = MboxConfig(mboxDirs=[MboxDir()]) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_015(self): """ Test comparison of two differing objects, mboxDirs differs (one empty, one not empty). """ mbox1 = MboxConfig("daily", "gzip", None, []) mbox2 = MboxConfig("daily", "gzip", None, [MboxDir()]) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) def testComparison_016(self): """ Test comparison of two differing objects, mboxDirs differs (both not empty). """ mbox1 = MboxConfig("daily", "gzip", None, [MboxDir()]) mbox2 = MboxConfig("daily", "gzip", None, [MboxDir(), MboxDir()]) self.assertNotEqual(mbox1, mbox2) self.assertTrue(not mbox1 == mbox2) self.assertTrue(mbox1 < mbox2) self.assertTrue(mbox1 <= mbox2) self.assertTrue(not mbox1 > mbox2) self.assertTrue(not mbox1 >= mbox2) self.assertTrue(mbox1 != mbox2) ######################## # TestLocalConfig class ######################## class TestLocalConfig(unittest.TestCase): """Tests for the LocalConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) def validateAddConfig(self, origConfig): """ Validates that document dumped from ``LocalConfig.addConfig`` results in identical object. We dump a document containing just the mbox configuration, and then make sure that if we push that document back into the ``LocalConfig`` object, that the resulting object matches the original. The ``self.failUnlessEqual`` method is used for the validation, so if the method call returns normally, everything is OK. Args: origConfig: Original configuration """ (xmlDom, parentNode) = createOutputDom() origConfig.addConfig(xmlDom, parentNode) xmlData = serializeDom(xmlDom) newConfig = LocalConfig(xmlData=xmlData, validate=False) self.assertEqual(origConfig, newConfig) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalConfig() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = LocalConfig(validate=False) self.assertEqual(None, config.mbox) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = LocalConfig(validate=True) self.assertEqual(None, config.mbox) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["mbox.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test assignment of mbox attribute, None value. """ config = LocalConfig() config.mbox = None self.assertEqual(None, config.mbox) def testConstructor_005(self): """ Test assignment of mbox attribute, valid value. """ config = LocalConfig() config.mbox = MboxConfig() self.assertEqual(MboxConfig(), config.mbox) def testConstructor_006(self): """ Test assignment of mbox attribute, invalid value (not MboxConfig). """ config = LocalConfig() self.failUnlessAssignRaises(ValueError, config, "mbox", "STRING!") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = LocalConfig() config2 = LocalConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = LocalConfig() config1.mbox = MboxConfig() config2 = LocalConfig() config2.mbox = MboxConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, mbox differs (one None). """ config1 = LocalConfig() config2 = LocalConfig() config2.mbox = MboxConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, mbox differs. """ config1 = LocalConfig() config1.mbox = MboxConfig(collectMode="daily") config2 = LocalConfig() config2.mbox = MboxConfig(collectMode="weekly") self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on a None mbox section. """ config = LocalConfig() config.mbox = None self.assertRaises(ValueError, config.validate) def testValidate_002(self): """ Test validate on an empty mbox section. """ config = LocalConfig() config.mbox = MboxConfig() self.assertRaises(ValueError, config.validate) def testValidate_003(self): """ Test validate on a non-empty mbox section, mboxFiles=None and mboxDirs=None. """ config = LocalConfig() config.mbox = MboxConfig("weekly", "gzip", None, None) self.assertRaises(ValueError, config.validate) def testValidate_004(self): """ Test validate on a non-empty mbox section, mboxFiles=[] and mboxDirs=[]. """ config = LocalConfig() config.mbox = MboxConfig("weekly", "gzip", [], []) self.assertRaises(ValueError, config.validate) def testValidate_005(self): """ Test validate on a non-empty mbox section, non-empty mboxFiles, defaults set, no values on files. """ mboxFiles = [MboxFile(absolutePath="/one"), MboxFile(absolutePath="/two")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "daily" config.mbox.compressMode = "gzip" config.mbox.mboxFiles = mboxFiles config.mbox.mboxDirs = None config.validate() def testValidate_006(self): """ Test validate on a non-empty mbox section, non-empty mboxDirs, defaults set, no values on directories. """ mboxDirs = [MboxDir(absolutePath="/one"), MboxDir(absolutePath="/two")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "daily" config.mbox.compressMode = "gzip" config.mbox.mboxFiles = None config.mbox.mboxDirs = mboxDirs config.validate() def testValidate_007(self): """ Test validate on a non-empty mbox section, non-empty mboxFiles, no defaults set, no values on files. """ mboxFiles = [MboxFile(absolutePath="/one"), MboxFile(absolutePath="/two")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.mboxFiles = mboxFiles config.mbox.mboxDirs = None self.assertRaises(ValueError, config.validate) def testValidate_008(self): """ Test validate on a non-empty mbox section, non-empty mboxDirs, no defaults set, no values on directories. """ mboxDirs = [MboxDir(absolutePath="/one"), MboxDir(absolutePath="/two")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.mboxFiles = None config.mbox.mboxDirs = mboxDirs self.assertRaises(ValueError, config.validate) def testValidate_009(self): """ Test validate on a non-empty mbox section, non-empty mboxFiles, no defaults set, both values on files. """ mboxFiles = [MboxFile(absolutePath="/two", collectMode="weekly", compressMode="gzip")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.mboxFiles = mboxFiles config.mbox.mboxDirs = None config.validate() def testValidate_010(self): """ Test validate on a non-empty mbox section, non-empty mboxDirs, no defaults set, both values on directories. """ mboxDirs = [MboxDir(absolutePath="/two", collectMode="weekly", compressMode="gzip")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.mboxFiles = None config.mbox.mboxDirs = mboxDirs config.validate() def testValidate_011(self): """ Test validate on a non-empty mbox section, non-empty mboxFiles, collectMode only on files. """ mboxFiles = [MboxFile(absolutePath="/two", collectMode="weekly")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.compressMode = "gzip" config.mbox.mboxFiles = mboxFiles config.mbox.mboxDirs = None config.validate() def testValidate_012(self): """ Test validate on a non-empty mbox section, non-empty mboxDirs, collectMode only on directories. """ mboxDirs = [MboxDir(absolutePath="/two", collectMode="weekly")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.compressMode = "gzip" config.mbox.mboxFiles = None config.mbox.mboxDirs = mboxDirs config.validate() def testValidate_013(self): """ Test validate on a non-empty mbox section, non-empty mboxFiles, compressMode only on files. """ mboxFiles = [MboxFile(absolutePath="/two", compressMode="bzip2")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "weekly" config.mbox.mboxFiles = mboxFiles config.mbox.mboxDirs = None config.validate() def testValidate_014(self): """ Test validate on a non-empty mbox section, non-empty mboxDirs, compressMode only on directories. """ mboxDirs = [MboxDir(absolutePath="/two", compressMode="bzip2")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "weekly" config.mbox.mboxFiles = None config.mbox.mboxDirs = mboxDirs config.validate() def testValidate_015(self): """ Test validate on a non-empty mbox section, non-empty mboxFiles, compressMode default and on files. """ mboxFiles = [MboxFile(absolutePath="/two", compressMode="bzip2")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "daily" config.mbox.compressMode = "gzip" config.mbox.mboxFiles = mboxFiles config.mbox.mboxDirs = None config.validate() def testValidate_016(self): """ Test validate on a non-empty mbox section, non-empty mboxDirs, compressMode default and on directories. """ mboxDirs = [MboxDir(absolutePath="/two", compressMode="bzip2")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "daily" config.mbox.compressMode = "gzip" config.mbox.mboxFiles = None config.mbox.mboxDirs = mboxDirs config.validate() def testValidate_017(self): """ Test validate on a non-empty mbox section, non-empty mboxFiles, collectMode default and on files. """ mboxFiles = [MboxFile(absolutePath="/two", collectMode="daily")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "daily" config.mbox.compressMode = "gzip" config.mbox.mboxFiles = mboxFiles config.mbox.mboxDirs = None config.validate() def testValidate_018(self): """ Test validate on a non-empty mbox section, non-empty mboxDirs, collectMode default and on directories. """ mboxDirs = [MboxDir(absolutePath="/two", collectMode="daily")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "daily" config.mbox.compressMode = "gzip" config.mbox.mboxFiles = None config.mbox.mboxDirs = mboxDirs config.validate() def testValidate_019(self): """ Test validate on a non-empty mbox section, non-empty mboxFiles, collectMode and compressMode default and on files. """ mboxFiles = [MboxFile(absolutePath="/two", collectMode="daily", compressMode="bzip2")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "daily" config.mbox.compressMode = "gzip" config.mbox.mboxFiles = mboxFiles config.mbox.mboxDirs = None config.validate() def testValidate_020(self): """ Test validate on a non-empty mbox section, non-empty mboxDirs, collectMode and compressMode default and on directories. """ mboxDirs = [MboxDir(absolutePath="/two", collectMode="daily", compressMode="bzip2")] config = LocalConfig() config.mbox = MboxConfig() config.mbox.collectMode = "daily" config.mbox.compressMode = "gzip" config.mbox.mboxFiles = None config.mbox.mboxDirs = mboxDirs config.validate() ############################ # Test parsing of documents ############################ def testParse_001(self): """ Parse empty config document. """ path = self.resources["mbox.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlPath=path, validate=True) self.assertRaises(ValueError, LocalConfig, xmlData=contents, validate=True) config = LocalConfig(xmlPath=path, validate=False) self.assertEqual(None, config.mbox) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual(None, config.mbox) def testParse_002(self): """ Parse config document with default modes, one collect file and one collect dir. """ mboxFiles = [ MboxFile(absolutePath="/home/joebob/mail/cedar-backup-users"), ] mboxDirs = [ MboxDir(absolutePath="/home/billiejoe/mail"), ] path = self.resources["mbox.conf.2"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.mbox) self.assertEqual("daily", config.mbox.collectMode) self.assertEqual("gzip", config.mbox.compressMode) self.assertEqual(mboxFiles, config.mbox.mboxFiles) self.assertEqual(mboxDirs, config.mbox.mboxDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.mbox) self.assertEqual("daily", config.mbox.collectMode) self.assertEqual("gzip", config.mbox.compressMode) self.assertEqual(mboxFiles, config.mbox.mboxFiles) self.assertEqual(mboxDirs, config.mbox.mboxDirs) def testParse_003(self): """ Parse config document with no default modes, one collect file and one collect dir. """ mboxFiles = [ MboxFile(absolutePath="/home/joebob/mail/cedar-backup-users", collectMode="daily", compressMode="gzip"), ] mboxDirs = [ MboxDir(absolutePath="/home/billiejoe/mail", collectMode="weekly", compressMode="bzip2"), ] path = self.resources["mbox.conf.3"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.mbox) self.assertEqual(None, config.mbox.collectMode) self.assertEqual(None, config.mbox.compressMode) self.assertEqual(mboxFiles, config.mbox.mboxFiles) self.assertEqual(mboxDirs, config.mbox.mboxDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.mbox) self.assertEqual(None, config.mbox.collectMode) self.assertEqual(None, config.mbox.compressMode) self.assertEqual(mboxFiles, config.mbox.mboxFiles) self.assertEqual(mboxDirs, config.mbox.mboxDirs) def testParse_004(self): """ Parse config document with default modes, several files with various overrides and exclusions. """ mboxFiles = [] mboxFile = MboxFile(absolutePath="/home/jimbo/mail/cedar-backup-users") mboxFiles.append(mboxFile) mboxFile = MboxFile(absolutePath="/home/joebob/mail/cedar-backup-users", collectMode="daily", compressMode="gzip") mboxFiles.append(mboxFile) mboxDirs = [] mboxDir = MboxDir(absolutePath="/home/frank/mail/cedar-backup-users") mboxDirs.append(mboxDir) mboxDir = MboxDir(absolutePath="/home/jimbob/mail", compressMode="bzip2", relativeExcludePaths=["logomachy-devel"]) mboxDirs.append(mboxDir) mboxDir = MboxDir( absolutePath="/home/billiejoe/mail", collectMode="weekly", compressMode="bzip2", excludePatterns=[".*SPAM.*"] ) mboxDirs.append(mboxDir) mboxDir = MboxDir( absolutePath="/home/billybob/mail", relativeExcludePaths=["debian-devel", "debian-python"], excludePatterns=[".*SPAM.*", ".*JUNK.*"], ) mboxDirs.append(mboxDir) path = self.resources["mbox.conf.4"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.mbox) self.assertEqual("incr", config.mbox.collectMode) self.assertEqual("none", config.mbox.compressMode) self.assertEqual(mboxFiles, config.mbox.mboxFiles) self.assertEqual(mboxDirs, config.mbox.mboxDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.mbox) self.assertEqual("incr", config.mbox.collectMode) self.assertEqual("none", config.mbox.compressMode) self.assertEqual(mboxFiles, config.mbox.mboxFiles) self.assertEqual(mboxDirs, config.mbox.mboxDirs) ################### # Test addConfig() ################### def testAddConfig_001(self): """ Test with empty config document. """ mbox = MboxConfig() config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_002(self): """ Test with defaults set, single mbox file with no optional values. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path")) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_003(self): """ Test with defaults set, single mbox directory with no optional values. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path")) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_004(self): """ Test with defaults set, single mbox file with collectMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path", collectMode="incr")) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_005(self): """ Test with defaults set, single mbox directory with collectMode set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", collectMode="incr")) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_006(self): """ Test with defaults set, single mbox file with compressMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path", compressMode="bzip2")) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_007(self): """ Test with defaults set, single mbox directory with compressMode set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", compressMode="bzip2")) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_008(self): """ Test with defaults set, single mbox file with collectMode and compressMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path", collectMode="weekly", compressMode="bzip2")) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_009(self): """ Test with defaults set, single mbox directory with collectMode and compressMode set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", collectMode="weekly", compressMode="bzip2")) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_010(self): """ Test with no defaults set, single mbox file with collectMode and compressMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path", collectMode="weekly", compressMode="bzip2")) mbox = MboxConfig(mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_011(self): """ Test with no defaults set, single mbox directory with collectMode and compressMode set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", collectMode="weekly", compressMode="bzip2")) mbox = MboxConfig(mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_012(self): """ Test with compressMode set, single mbox file with collectMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path", collectMode="weekly")) mbox = MboxConfig(compressMode="gzip", mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_013(self): """ Test with compressMode set, single mbox directory with collectMode set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", collectMode="weekly")) mbox = MboxConfig(compressMode="gzip", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_014(self): """ Test with collectMode set, single mbox file with compressMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path", compressMode="gzip")) mbox = MboxConfig(collectMode="weekly", mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_015(self): """ Test with collectMode set, single mbox directory with compressMode set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", compressMode="gzip")) mbox = MboxConfig(collectMode="weekly", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_016(self): """ Test with compressMode set, single mbox file with collectMode and compressMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path", collectMode="incr", compressMode="gzip")) mbox = MboxConfig(compressMode="bzip2", mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_017(self): """ Test with compressMode set, single mbox directory with collectMode and compressMode set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", collectMode="incr", compressMode="gzip")) mbox = MboxConfig(compressMode="bzip2", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_018(self): """ Test with collectMode set, single mbox file with collectMode and compressMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path", collectMode="weekly", compressMode="gzip")) mbox = MboxConfig(collectMode="incr", mboxFiles=mboxFiles) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_019(self): """ Test with collectMode set, single mbox directory with collectMode and compressMode set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", collectMode="weekly", compressMode="gzip")) mbox = MboxConfig(collectMode="incr", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_020(self): """ Test with defaults set, single mbox directory with relativeExcludePaths set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", relativeExcludePaths=["one", "two"])) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_021(self): """ Test with defaults set, single mbox directory with excludePatterns set. """ mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path", excludePatterns=["one", "two"])) mbox = MboxConfig(collectMode="daily", compressMode="gzip", mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) def testAddConfig_022(self): """ Test with defaults set, multiple mbox files and directories with collectMode and compressMode set. """ mboxFiles = [] mboxFiles.append(MboxFile(absolutePath="/path1", collectMode="daily", compressMode="gzip")) mboxFiles.append(MboxFile(absolutePath="/path2", collectMode="weekly", compressMode="gzip")) mboxFiles.append(MboxFile(absolutePath="/path3", collectMode="incr", compressMode="gzip")) mboxDirs = [] mboxDirs.append(MboxDir(absolutePath="/path1", collectMode="daily", compressMode="bzip2")) mboxDirs.append(MboxDir(absolutePath="/path2", collectMode="weekly", compressMode="bzip2")) mboxDirs.append(MboxDir(absolutePath="/path3", collectMode="incr", compressMode="bzip2")) mbox = MboxConfig(collectMode="incr", compressMode="bzip2", mboxFiles=mboxFiles, mboxDirs=mboxDirs) config = LocalConfig() config.mbox = mbox self.validateAddConfig(config) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1609564 cedar_backup3-3.8.1/tests/test_mysql.py0000644000000000000000000012001414567004737015072 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2005-2006,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests MySQL extension functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/extend/mysql.py. Code Coverage ============= This module contains individual tests for the many of the public functions and classes implemented in extend/mysql.py. There are also tests for several of the private methods. Unfortunately, it's rather difficult to test this code in an automated fashion, even if you have access to MySQL, since the actual dump would need to have access to a real database. Because of this, there aren't any tests below that actually talk to a database. As a compromise, I test some of the private methods in the implementation. Normally, I don't like to test private methods, but in this case, testing the private methods will help give us some reasonable confidence in the code even if we can't talk to a database.. This isn't perfect, but it's better than nothing. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract a node, build some XML from it, and then feed that XML back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a MYSQLTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.extend.mysql import LocalConfig, MysqlConfig from CedarBackup3.testutil import configureLogging, failUnlessAssignRaises, findResources from CedarBackup3.xmlutil import createOutputDom, serializeDom ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "mysql.conf.1", "mysql.conf.2", "mysql.conf.3", "mysql.conf.4", "mysql.conf.5", ] ####################################################################### # Test Case Classes ####################################################################### ######################## # TestMysqlConfig class ######################## class TestMysqlConfig(unittest.TestCase): """Tests for the MysqlConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = MysqlConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ mysql = MysqlConfig() self.assertEqual(None, mysql.user) self.assertEqual(None, mysql.password) self.assertEqual(None, mysql.compressMode) self.assertEqual(False, mysql.all) self.assertEqual(None, mysql.databases) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values, databases=None. """ mysql = MysqlConfig("user", "password", "none", False, None) self.assertEqual("user", mysql.user) self.assertEqual("password", mysql.password) self.assertEqual("none", mysql.compressMode) self.assertEqual(False, mysql.all) self.assertEqual(None, mysql.databases) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values, no databases. """ mysql = MysqlConfig("user", "password", "none", True, []) self.assertEqual("user", mysql.user) self.assertEqual("password", mysql.password) self.assertEqual("none", mysql.compressMode) self.assertEqual(True, mysql.all) self.assertEqual([], mysql.databases) def testConstructor_004(self): """ Test constructor with all values filled in, with valid values, with one database. """ mysql = MysqlConfig("user", "password", "gzip", True, ["one"]) self.assertEqual("user", mysql.user) self.assertEqual("password", mysql.password) self.assertEqual("gzip", mysql.compressMode) self.assertEqual(True, mysql.all) self.assertEqual(["one"], mysql.databases) def testConstructor_005(self): """ Test constructor with all values filled in, with valid values, with multiple databases. """ mysql = MysqlConfig("user", "password", "bzip2", True, ["one", "two"]) self.assertEqual("user", mysql.user) self.assertEqual("password", mysql.password) self.assertEqual("bzip2", mysql.compressMode) self.assertEqual(True, mysql.all) self.assertEqual(["one", "two"], mysql.databases) def testConstructor_006(self): """ Test assignment of user attribute, None value. """ mysql = MysqlConfig(user="user") self.assertEqual("user", mysql.user) mysql.user = None self.assertEqual(None, mysql.user) def testConstructor_007(self): """ Test assignment of user attribute, valid value. """ mysql = MysqlConfig() self.assertEqual(None, mysql.user) mysql.user = "user" self.assertEqual("user", mysql.user) def testConstructor_008(self): """ Test assignment of user attribute, invalid value (empty). """ mysql = MysqlConfig() self.assertEqual(None, mysql.user) self.failUnlessAssignRaises(ValueError, mysql, "user", "") self.assertEqual(None, mysql.user) def testConstructor_009(self): """ Test assignment of password attribute, None value. """ mysql = MysqlConfig(password="password") self.assertEqual("password", mysql.password) mysql.password = None self.assertEqual(None, mysql.password) def testConstructor_010(self): """ Test assignment of password attribute, valid value. """ mysql = MysqlConfig() self.assertEqual(None, mysql.password) mysql.password = "password" self.assertEqual("password", mysql.password) def testConstructor_011(self): """ Test assignment of password attribute, invalid value (empty). """ mysql = MysqlConfig() self.assertEqual(None, mysql.password) self.failUnlessAssignRaises(ValueError, mysql, "password", "") self.assertEqual(None, mysql.password) def testConstructor_012(self): """ Test assignment of compressMode attribute, None value. """ mysql = MysqlConfig(compressMode="none") self.assertEqual("none", mysql.compressMode) mysql.compressMode = None self.assertEqual(None, mysql.compressMode) def testConstructor_013(self): """ Test assignment of compressMode attribute, valid value. """ mysql = MysqlConfig() self.assertEqual(None, mysql.compressMode) mysql.compressMode = "none" self.assertEqual("none", mysql.compressMode) mysql.compressMode = "gzip" self.assertEqual("gzip", mysql.compressMode) mysql.compressMode = "bzip2" self.assertEqual("bzip2", mysql.compressMode) def testConstructor_014(self): """ Test assignment of compressMode attribute, invalid value (empty). """ mysql = MysqlConfig() self.assertEqual(None, mysql.compressMode) self.failUnlessAssignRaises(ValueError, mysql, "compressMode", "") self.assertEqual(None, mysql.compressMode) def testConstructor_015(self): """ Test assignment of compressMode attribute, invalid value (not in list). """ mysql = MysqlConfig() self.assertEqual(None, mysql.compressMode) self.failUnlessAssignRaises(ValueError, mysql, "compressMode", "bogus") self.assertEqual(None, mysql.compressMode) def testConstructor_016(self): """ Test assignment of all attribute, None value. """ mysql = MysqlConfig(all=True) self.assertEqual(True, mysql.all) mysql.all = None self.assertEqual(False, mysql.all) def testConstructor_017(self): """ Test assignment of all attribute, valid value (real boolean). """ mysql = MysqlConfig() self.assertEqual(False, mysql.all) mysql.all = True self.assertEqual(True, mysql.all) mysql.all = False self.assertEqual(False, mysql.all) def testConstructor_018(self): """ Test assignment of all attribute, valid value (expression). """ mysql = MysqlConfig() self.assertEqual(False, mysql.all) mysql.all = 0 self.assertEqual(False, mysql.all) mysql.all = [] self.assertEqual(False, mysql.all) mysql.all = None self.assertEqual(False, mysql.all) mysql.all = ["a"] self.assertEqual(True, mysql.all) mysql.all = 3 self.assertEqual(True, mysql.all) def testConstructor_019(self): """ Test assignment of databases attribute, None value. """ mysql = MysqlConfig(databases=[]) self.assertEqual([], mysql.databases) mysql.databases = None self.assertEqual(None, mysql.databases) def testConstructor_020(self): """ Test assignment of databases attribute, [] value. """ mysql = MysqlConfig() self.assertEqual(None, mysql.databases) mysql.databases = [] self.assertEqual([], mysql.databases) def testConstructor_021(self): """ Test assignment of databases attribute, single valid entry. """ mysql = MysqlConfig() self.assertEqual(None, mysql.databases) mysql.databases = [ "/whatever", ] self.assertEqual(["/whatever"], mysql.databases) mysql.databases.append("/stuff") self.assertEqual(["/whatever", "/stuff"], mysql.databases) def testConstructor_022(self): """ Test assignment of databases attribute, multiple valid entries. """ mysql = MysqlConfig() self.assertEqual(None, mysql.databases) mysql.databases = [ "/whatever", "/stuff", ] self.assertEqual(["/whatever", "/stuff"], mysql.databases) mysql.databases.append("/etc/X11") self.assertEqual(["/whatever", "/stuff", "/etc/X11"], mysql.databases) def testConstructor_023(self): """ Test assignment of databases attribute, single invalid entry (empty). """ mysql = MysqlConfig() self.assertEqual(None, mysql.databases) self.failUnlessAssignRaises(ValueError, mysql, "databases", [""]) self.assertEqual(None, mysql.databases) def testConstructor_024(self): """ Test assignment of databases attribute, mixed valid and invalid entries. """ mysql = MysqlConfig() self.assertEqual(None, mysql.databases) self.failUnlessAssignRaises(ValueError, mysql, "databases", ["good", "", "alsogood"]) self.assertEqual(None, mysql.databases) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ mysql1 = MysqlConfig() mysql2 = MysqlConfig() self.assertEqual(mysql1, mysql2) self.assertTrue(mysql1 == mysql2) self.assertTrue(not mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(mysql1 >= mysql2) self.assertTrue(not mysql1 != mysql2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None, list None. """ mysql1 = MysqlConfig("user", "password", "gzip", True, None) mysql2 = MysqlConfig("user", "password", "gzip", True, None) self.assertEqual(mysql1, mysql2) self.assertTrue(mysql1 == mysql2) self.assertTrue(not mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(mysql1 >= mysql2) self.assertTrue(not mysql1 != mysql2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None, list empty. """ mysql1 = MysqlConfig("user", "password", "bzip2", True, []) mysql2 = MysqlConfig("user", "password", "bzip2", True, []) self.assertEqual(mysql1, mysql2) self.assertTrue(mysql1 == mysql2) self.assertTrue(not mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(mysql1 >= mysql2) self.assertTrue(not mysql1 != mysql2) def testComparison_004(self): """ Test comparison of two identical objects, all attributes non-None, list non-empty. """ mysql1 = MysqlConfig("user", "password", "none", True, ["whatever"]) mysql2 = MysqlConfig("user", "password", "none", True, ["whatever"]) self.assertEqual(mysql1, mysql2) self.assertTrue(mysql1 == mysql2) self.assertTrue(not mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(mysql1 >= mysql2) self.assertTrue(not mysql1 != mysql2) def testComparison_005(self): """ Test comparison of two differing objects, user differs (one None). """ mysql1 = MysqlConfig() mysql2 = MysqlConfig(user="user") self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_006(self): """ Test comparison of two differing objects, user differs. """ mysql1 = MysqlConfig("user1", "password", "gzip", True, ["whatever"]) mysql2 = MysqlConfig("user2", "password", "gzip", True, ["whatever"]) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_007(self): """ Test comparison of two differing objects, password differs (one None). """ mysql1 = MysqlConfig() mysql2 = MysqlConfig(password="password") self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_008(self): """ Test comparison of two differing objects, password differs. """ mysql1 = MysqlConfig("user", "password1", "gzip", True, ["whatever"]) mysql2 = MysqlConfig("user", "password2", "gzip", True, ["whatever"]) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_009(self): """ Test comparison of two differing objects, compressMode differs (one None). """ mysql1 = MysqlConfig() mysql2 = MysqlConfig(compressMode="gzip") self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_010(self): """ Test comparison of two differing objects, compressMode differs. """ mysql1 = MysqlConfig("user", "password", "bzip2", True, ["whatever"]) mysql2 = MysqlConfig("user", "password", "gzip", True, ["whatever"]) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_011(self): """ Test comparison of two differing objects, all differs (one None). """ mysql1 = MysqlConfig() mysql2 = MysqlConfig(all=True) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_012(self): """ Test comparison of two differing objects, all differs. """ mysql1 = MysqlConfig("user", "password", "gzip", False, ["whatever"]) mysql2 = MysqlConfig("user", "password", "gzip", True, ["whatever"]) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_013(self): """ Test comparison of two differing objects, databases differs (one None, one empty). """ mysql1 = MysqlConfig() mysql2 = MysqlConfig(databases=[]) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_014(self): """ Test comparison of two differing objects, databases differs (one None, one not empty). """ mysql1 = MysqlConfig() mysql2 = MysqlConfig(databases=["whatever"]) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_015(self): """ Test comparison of two differing objects, databases differs (one empty, one not empty). """ mysql1 = MysqlConfig("user", "password", "gzip", True, []) mysql2 = MysqlConfig("user", "password", "gzip", True, ["whatever"]) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(mysql1 < mysql2) self.assertTrue(mysql1 <= mysql2) self.assertTrue(not mysql1 > mysql2) self.assertTrue(not mysql1 >= mysql2) self.assertTrue(mysql1 != mysql2) def testComparison_016(self): """ Test comparison of two differing objects, databases differs (both not empty). """ mysql1 = MysqlConfig("user", "password", "gzip", True, ["whatever"]) mysql2 = MysqlConfig("user", "password", "gzip", True, ["whatever", "bogus"]) self.assertNotEqual(mysql1, mysql2) self.assertTrue(not mysql1 == mysql2) self.assertTrue(not mysql1 < mysql2) # note: different than standard due to unsorted list self.assertTrue(not mysql1 <= mysql2) # note: different than standard due to unsorted list self.assertTrue(mysql1 > mysql2) # note: different than standard due to unsorted list self.assertTrue(mysql1 >= mysql2) # note: different than standard due to unsorted list self.assertTrue(mysql1 != mysql2) ######################## # TestLocalConfig class ######################## class TestLocalConfig(unittest.TestCase): """Tests for the LocalConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) def validateAddConfig(self, origConfig): """ Validates that document dumped from ``LocalConfig.addConfig`` results in identical object. We dump a document containing just the mysql configuration, and then make sure that if we push that document back into the ``LocalConfig`` object, that the resulting object matches the original. The ``self.failUnlessEqual`` method is used for the validation, so if the method call returns normally, everything is OK. Args: origConfig: Original configuration """ (xmlDom, parentNode) = createOutputDom() origConfig.addConfig(xmlDom, parentNode) xmlData = serializeDom(xmlDom) newConfig = LocalConfig(xmlData=xmlData, validate=False) self.assertEqual(origConfig, newConfig) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalConfig() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = LocalConfig(validate=False) self.assertEqual(None, config.mysql) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = LocalConfig(validate=True) self.assertEqual(None, config.mysql) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["mysql.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test assignment of mysql attribute, None value. """ config = LocalConfig() config.mysql = None self.assertEqual(None, config.mysql) def testConstructor_005(self): """ Test assignment of mysql attribute, valid value. """ config = LocalConfig() config.mysql = MysqlConfig() self.assertEqual(MysqlConfig(), config.mysql) def testConstructor_006(self): """ Test assignment of mysql attribute, invalid value (not MysqlConfig). """ config = LocalConfig() self.failUnlessAssignRaises(ValueError, config, "mysql", "STRING!") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = LocalConfig() config2 = LocalConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = LocalConfig() config1.mysql = MysqlConfig() config2 = LocalConfig() config2.mysql = MysqlConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, mysql differs (one None). """ config1 = LocalConfig() config2 = LocalConfig() config2.mysql = MysqlConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, mysql differs. """ config1 = LocalConfig() config1.mysql = MysqlConfig(user="one") config2 = LocalConfig() config2.mysql = MysqlConfig(user="two") self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on a None mysql section. """ config = LocalConfig() config.mysql = None self.assertRaises(ValueError, config.validate) def testValidate_002(self): """ Test validate on an empty mysql section. """ config = LocalConfig() config.mysql = MysqlConfig() self.assertRaises(ValueError, config.validate) def testValidate_003(self): """ Test validate on a non-empty mysql section, all=True, databases=None. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "gzip", True, None) config.validate() def testValidate_004(self): """ Test validate on a non-empty mysql section, all=True, empty databases. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "none", True, []) config.validate() def testValidate_005(self): """ Test validate on a non-empty mysql section, all=True, non-empty databases. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "bzip2", True, ["whatever"]) self.assertRaises(ValueError, config.validate) def testValidate_006(self): """ Test validate on a non-empty mysql section, all=False, databases=None. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "gzip", False, None) self.assertRaises(ValueError, config.validate) def testValidate_007(self): """ Test validate on a non-empty mysql section, all=False, empty databases. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "bzip2", False, []) self.assertRaises(ValueError, config.validate) def testValidate_008(self): """ Test validate on a non-empty mysql section, all=False, non-empty databases. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "gzip", False, ["whatever"]) config.validate() def testValidate_009(self): """ Test validate on a non-empty mysql section, with user=None. """ config = LocalConfig() config.mysql = MysqlConfig(None, "password", "gzip", True, None) config.validate() def testValidate_010(self): """ Test validate on a non-empty mysql section, with password=None. """ config = LocalConfig() config.mysql = MysqlConfig("user", None, "gzip", True, None) config.validate() def testValidate_011(self): """ Test validate on a non-empty mysql section, with user=None and password=None. """ config = LocalConfig() config.mysql = MysqlConfig(None, None, "gzip", True, None) config.validate() ############################ # Test parsing of documents ############################ def testParse_001(self): """ Parse empty config document. """ path = self.resources["mysql.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlPath=path, validate=True) self.assertRaises(ValueError, LocalConfig, xmlData=contents, validate=True) config = LocalConfig(xmlPath=path, validate=False) self.assertEqual(None, config.mysql) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual(None, config.mysql) def testParse_003(self): """ Parse config document containing only a mysql section, no databases, all=True. """ path = self.resources["mysql.conf.2"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.mysql) self.assertEqual("user", config.mysql.user) self.assertEqual("password", config.mysql.password) self.assertEqual("none", config.mysql.compressMode) self.assertEqual(True, config.mysql.all) self.assertEqual(None, config.mysql.databases) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual("user", config.mysql.user) self.assertEqual("password", config.mysql.password) self.assertEqual("none", config.mysql.compressMode) self.assertNotEqual(None, config.mysql.password) self.assertEqual(True, config.mysql.all) self.assertEqual(None, config.mysql.databases) def testParse_004(self): """ Parse config document containing only a mysql section, single database, all=False. """ path = self.resources["mysql.conf.3"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.mysql) self.assertEqual("user", config.mysql.user) self.assertEqual("password", config.mysql.password) self.assertEqual("gzip", config.mysql.compressMode) self.assertEqual(False, config.mysql.all) self.assertEqual(["database"], config.mysql.databases) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.mysql) self.assertEqual("user", config.mysql.user) self.assertEqual("password", config.mysql.password) self.assertEqual("gzip", config.mysql.compressMode) self.assertEqual(False, config.mysql.all) self.assertEqual(["database"], config.mysql.databases) def testParse_005(self): """ Parse config document containing only a mysql section, multiple databases, all=False. """ path = self.resources["mysql.conf.4"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.mysql) self.assertEqual("user", config.mysql.user) self.assertEqual("password", config.mysql.password) self.assertEqual("bzip2", config.mysql.compressMode) self.assertEqual(False, config.mysql.all) self.assertEqual(["database1", "database2"], config.mysql.databases) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.mysql) self.assertEqual("user", config.mysql.user) self.assertEqual("password", config.mysql.password) self.assertEqual("bzip2", config.mysql.compressMode) self.assertEqual(False, config.mysql.all) self.assertEqual(["database1", "database2"], config.mysql.databases) def testParse_006(self): """ Parse config document containing only a mysql section, no user or password, multiple databases, all=False. """ path = self.resources["mysql.conf.5"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.mysql) self.assertEqual(None, config.mysql.user) self.assertEqual(None, config.mysql.password) self.assertEqual("bzip2", config.mysql.compressMode) self.assertEqual(False, config.mysql.all) self.assertEqual(["database1", "database2"], config.mysql.databases) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.mysql) self.assertEqual(None, config.mysql.user) self.assertEqual(None, config.mysql.password) self.assertEqual("bzip2", config.mysql.compressMode) self.assertEqual(False, config.mysql.all) self.assertEqual(["database1", "database2"], config.mysql.databases) ################### # Test addConfig() ################### def testAddConfig_001(self): """ Test with empty config document """ config = LocalConfig() self.validateAddConfig(config) def testAddConfig_003(self): """ Test with no databases, all other values filled in, all=True. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "none", True, None) self.validateAddConfig(config) def testAddConfig_004(self): """ Test with no databases, all other values filled in, all=False. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "gzip", False, None) self.validateAddConfig(config) def testAddConfig_005(self): """ Test with single database, all other values filled in, all=True. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "bzip2", True, ["database"]) self.validateAddConfig(config) def testAddConfig_006(self): """ Test with single database, all other values filled in, all=False. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "none", False, ["database"]) self.validateAddConfig(config) def testAddConfig_007(self): """ Test with multiple databases, all other values filled in, all=True. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "bzip2", True, ["database1", "database2"]) self.validateAddConfig(config) def testAddConfig_008(self): """ Test with multiple databases, all other values filled in, all=False. """ config = LocalConfig() config.mysql = MysqlConfig("user", "password", "gzip", True, ["database1", "database2"]) self.validateAddConfig(config) def testAddConfig_009(self): """ Test with multiple databases, user=None but all other values filled in, all=False. """ config = LocalConfig() config.mysql = MysqlConfig(None, "password", "gzip", True, ["database1", "database2"]) self.validateAddConfig(config) def testAddConfig_010(self): """ Test with multiple databases, password=None but all other values filled in, all=False. """ config = LocalConfig() config.mysql = MysqlConfig("user", None, "gzip", True, ["database1", "database2"]) self.validateAddConfig(config) def testAddConfig_011(self): """ Test with multiple databases, user=None and password=None but all other values filled in, all=False. """ config = LocalConfig() config.mysql = MysqlConfig(None, None, "gzip", True, ["database1", "database2"]) self.validateAddConfig(config) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1649563 cedar_backup3-3.8.1/tests/test_peer.py0000644000000000000000000020515614567004737014673 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests peer functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/peer.py. Code Coverage ============= This module contains individual tests for most of the public functions and classes implemented in peer.py, including the ``LocalPeer`` and ``RemotePeer`` classes. Unfortunately, some of the code can't be tested. In particular, the stage code allows the caller to change ownership on files. Generally, this can only be done by root, and most people won't be running these tests as root. As such, we can't test this functionality. There are also some other pieces of functionality that can only be tested in certain environments (see below). Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== Some Cedar Backup regression tests require a specialized environment in order to run successfully. This environment won't necessarily be available on every build system out there (for instance, on a Debian autobuilder). Because of this, the default behavior is to run a "reduced feature set" test suite that has no surprising system, kernel or network requirements. If you want to run all of the tests, set PEERTESTS_FULL to "Y" in the environment. In this module, network-related testing is what causes us our biggest problems. In order to test the RemotePeer, we need a "remote" host that we can rcp to and from. We want to fall back on using localhost and the current user, but that might not be safe or appropriate. As such, we'll only run these tests if PEERTESTS_FULL is set to "Y" in the environment. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## # Import standard modules import os import stat import tempfile import unittest from CedarBackup3.peer import DEF_COLLECT_INDICATOR, DEF_RCP_COMMAND, DEF_RSH_COMMAND, DEF_STAGE_INDICATOR, LocalPeer, RemotePeer from CedarBackup3.testutil import ( buildPath, configureLogging, extractTar, failUnlessAssignRaises, findResources, getLogin, getMaskAsMode, platformWindows, removedir, ) from CedarBackup3.util import isRunningAsRoot, pathJoin ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "tree1.tar.gz", "tree2.tar.gz", "tree9.tar.gz", ] REMOTE_HOST = "localhost" # Always use login@localhost as our "remote" host NONEXISTENT_FILE = "bogus" # This file name should never exist NONEXISTENT_HOST = "hostname.invalid" # RFC 2606 reserves the ".invalid" TLD for "obviously invalid" names NONEXISTENT_USER = "unittestuser" # This user name should never exist on localhost NONEXISTENT_CMD = "/bogus/~~~ZZZZ/bad/not/there" # This command should never exist in the filesystem SAFE_RCP_COMMAND = "/usr/bin/scp -O -B -q -C -o ConnectTimeout=1" # set a connection timeout so invalid hosts don't hang SAFE_RSH_COMMAND = "/usr/bin/ssh -o ConnectTimeout=1" # set a connection timeout so invalid hosts don't hang ####################################################################### # Utility functions ####################################################################### def runAllTests(): """Returns true/false depending on whether the full test suite should be run.""" if "PEERTESTS_FULL" in os.environ: return os.environ["PEERTESTS_FULL"] == "Y" else: return False ####################################################################### # Test Case Classes ####################################################################### ###################### # TestLocalPeer class ###################### class TestLocalPeer(unittest.TestCase): """Tests for the LocalPeer class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) def getFileMode(self, components): """Calls buildPath on components and then returns file mode for the file.""" return stat.S_IMODE(os.stat(self.buildPath(components)).st_mode) def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ########################### # Test basic functionality ########################### def testBasic_001(self): """ Make sure exception is thrown for non-absolute collect directory. """ name = "peer1" collectDir = "whatever/something/else/not/absolute" self.assertRaises(ValueError, LocalPeer, name, collectDir) def testBasic_002(self): """ Make sure attributes are set properly for valid constructor input. """ name = "peer1" collectDir = "/absolute/path/name" ignoreFailureMode = "all" peer = LocalPeer(name, collectDir, ignoreFailureMode) self.assertEqual(name, peer.name) self.assertEqual(collectDir, peer.collectDir) self.assertEqual(ignoreFailureMode, peer.ignoreFailureMode) def testBasic_003(self): """ Make sure attributes are set properly for valid constructor input, with spaces in the collect directory path. """ name = "peer1" collectDir = "/ absolute / path/ name " peer = LocalPeer(name, collectDir) self.assertEqual(name, peer.name) self.assertEqual(collectDir, peer.collectDir) def testBasic_004(self): """ Make sure assignment works for all valid failure modes. """ name = "peer1" collectDir = "/absolute/path/name" ignoreFailureMode = "all" peer = LocalPeer(name, collectDir, ignoreFailureMode) self.assertEqual("all", peer.ignoreFailureMode) peer.ignoreFailureMode = "none" self.assertEqual("none", peer.ignoreFailureMode) peer.ignoreFailureMode = "daily" self.assertEqual("daily", peer.ignoreFailureMode) peer.ignoreFailureMode = "weekly" self.assertEqual("weekly", peer.ignoreFailureMode) self.failUnlessAssignRaises(ValueError, peer, "ignoreFailureMode", "bogus") ############################### # Test checkCollectIndicator() ############################### def testCheckCollectIndicator_001(self): """ Attempt to check collect indicator with non-existent collect directory. """ name = "peer1" collectDir = self.buildPath([NONEXISTENT_FILE]) self.assertTrue(not os.path.exists(collectDir)) peer = LocalPeer(name, collectDir) result = peer.checkCollectIndicator() self.assertEqual(False, result) def testCheckCollectIndicator_002(self): """ Attempt to check collect indicator with non-readable collect directory. """ name = "peer1" collectDir = self.buildPath(["collect"]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) os.chmod(collectDir, 0o200) # user can't read his own directory peer = LocalPeer(name, collectDir) result = peer.checkCollectIndicator() self.assertEqual(False, result) os.chmod(collectDir, 0o777) # so we can remove it safely def testCheckCollectIndicator_003(self): """ Attempt to check collect indicator collect indicator file that does not exist. """ name = "peer1" collectDir = self.buildPath(["collect"]) collectIndicator = self.buildPath(["collect", DEF_COLLECT_INDICATOR]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(collectIndicator)) peer = LocalPeer(name, collectDir) result = peer.checkCollectIndicator() self.assertEqual(False, result) def testCheckCollectIndicator_004(self): """ Attempt to check collect indicator collect indicator file that does not exist, custom name. """ name = "peer1" collectDir = self.buildPath(["collect"]) collectIndicator = self.buildPath(["collect", NONEXISTENT_FILE]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(collectIndicator)) peer = LocalPeer(name, collectDir) result = peer.checkCollectIndicator(collectIndicator=NONEXISTENT_FILE) self.assertEqual(False, result) def testCheckCollectIndicator_005(self): """ Attempt to check collect indicator collect indicator file that does exist. """ name = "peer1" collectDir = self.buildPath(["collect"]) collectIndicator = self.buildPath(["collect", DEF_COLLECT_INDICATOR]) os.mkdir(collectDir) with open(collectIndicator, "w") as f: f.write("") # touch the file self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(collectIndicator)) peer = LocalPeer(name, collectDir) result = peer.checkCollectIndicator() self.assertEqual(True, result) def testCheckCollectIndicator_006(self): """ Attempt to check collect indicator collect indicator file that does exist, custom name. """ name = "peer1" collectDir = self.buildPath(["collect"]) collectIndicator = self.buildPath(["collect", "different"]) os.mkdir(collectDir) with open(collectIndicator, "w") as f: f.write("") # touch the file self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(collectIndicator)) peer = LocalPeer(name, collectDir) result = peer.checkCollectIndicator(collectIndicator="different") self.assertEqual(True, result) def testCheckCollectIndicator_007(self): """ Attempt to check collect indicator collect indicator file that does exist, with spaces in the collect directory path. """ name = "peer1" collectDir = self.buildPath(["collect directory here"]) collectIndicator = self.buildPath(["collect directory here", DEF_COLLECT_INDICATOR]) os.mkdir(collectDir) with open(collectIndicator, "w") as f: f.write("") # touch the file self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(collectIndicator)) peer = LocalPeer(name, collectDir) result = peer.checkCollectIndicator() self.assertEqual(True, result) @unittest.skipIf(platformWindows(), "Behavior differs on Windows") def testCheckCollectIndicator_008(self): """ Attempt to check collect indicator collect indicator file that does exist, custom name, with spaces in the collect directory path and collect indicator file name. """ name = "peer1" collectDir = self.buildPath([" collect dir "]) collectIndicator = self.buildPath([" collect dir ", "different, file"]) os.mkdir(collectDir) with open(collectIndicator, "w") as f: f.write("") # touch the file self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(collectIndicator)) peer = LocalPeer(name, collectDir) result = peer.checkCollectIndicator(collectIndicator="different, file") self.assertEqual(True, result) ############################# # Test writeStageIndicator() ############################# def testWriteStageIndicator_001(self): """ Attempt to write stage indicator with non-existent collect directory. """ name = "peer1" collectDir = self.buildPath([NONEXISTENT_FILE]) self.assertTrue(not os.path.exists(collectDir)) peer = LocalPeer(name, collectDir) self.assertRaises(ValueError, peer.writeStageIndicator) @unittest.skipIf(platformWindows(), "Behavior differs on Windows") def testWriteStageIndicator_002(self): """ Attempt to write stage indicator with non-writable collect directory. """ if not isRunningAsRoot(): # root doesn't get this error name = "peer1" collectDir = self.buildPath(["collect"]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) os.chmod(collectDir, 0o500) # read-only for user peer = LocalPeer(name, collectDir) self.assertRaises((IOError, OSError), peer.writeStageIndicator) os.chmod(collectDir, 0o777) # so we can remove it safely @unittest.skipIf(platformWindows(), "Behavior differs on Windows") def testWriteStageIndicator_003(self): """ Attempt to write stage indicator with non-writable collect directory, custom name. """ if not isRunningAsRoot(): # root doesn't get this error name = "peer1" collectDir = self.buildPath(["collect"]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) os.chmod(collectDir, 0o500) # read-only for user peer = LocalPeer(name, collectDir) self.assertRaises((IOError, OSError), peer.writeStageIndicator, stageIndicator="something") os.chmod(collectDir, 0o777) # so we can remove it safely def testWriteStageIndicator_004(self): """ Attempt to write stage indicator in a valid directory. """ name = "peer1" collectDir = self.buildPath(["collect"]) stageIndicator = self.buildPath(["collect", DEF_STAGE_INDICATOR]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = LocalPeer(name, collectDir) peer.writeStageIndicator() self.assertTrue(os.path.exists(stageIndicator)) def testWriteStageIndicator_005(self): """ Attempt to write stage indicator in a valid directory, custom name. """ name = "peer1" collectDir = self.buildPath(["collect"]) stageIndicator = self.buildPath(["collect", "whatever"]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = LocalPeer(name, collectDir) peer.writeStageIndicator(stageIndicator="whatever") self.assertTrue(os.path.exists(stageIndicator)) def testWriteStageIndicator_006(self): """ Attempt to write stage indicator in a valid directory, with spaces in the directory name. """ name = "peer1" collectDir = self.buildPath(["collect from this directory"]) stageIndicator = self.buildPath(["collect from this directory", DEF_STAGE_INDICATOR]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = LocalPeer(name, collectDir) peer.writeStageIndicator() self.assertTrue(os.path.exists(stageIndicator)) def testWriteStageIndicator_007(self): """ Attempt to write stage indicator in a valid directory, custom name, with spaces in the directory name and the file name. """ name = "peer1" collectDir = self.buildPath(["collect ME"]) stageIndicator = self.buildPath(["collect ME", " whatever-it-takes you"]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = LocalPeer(name, collectDir) peer.writeStageIndicator(stageIndicator=" whatever-it-takes you") self.assertTrue(os.path.exists(stageIndicator)) ################### # Test stagePeer() ################### def testStagePeer_001(self): """ Attempt to stage files with non-existent collect directory. """ name = "peer1" collectDir = self.buildPath([NONEXISTENT_FILE]) targetDir = self.buildPath(["target"]) os.mkdir(targetDir) self.assertTrue(not os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = LocalPeer(name, collectDir) self.assertRaises(ValueError, peer.stagePeer, targetDir=targetDir) def testStagePeer_002(self): """ Attempt to stage files with non-readable collect directory. """ name = "peer1" collectDir = self.buildPath(["collect"]) targetDir = self.buildPath(["target"]) os.mkdir(collectDir) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) os.chmod(collectDir, 0o200) # user can't read his own directory peer = LocalPeer(name, collectDir) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) os.chmod(collectDir, 0o777) # so we can remove it safely def testStagePeer_003(self): """ Attempt to stage files with non-absolute target directory. """ name = "peer1" collectDir = self.buildPath(["collect"]) targetDir = "this/is/not/absolute" os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = LocalPeer(name, collectDir) self.assertRaises(ValueError, peer.stagePeer, targetDir=targetDir) def testStagePeer_004(self): """ Attempt to stage files with non-existent target directory. """ name = "peer1" collectDir = self.buildPath(["collect"]) targetDir = self.buildPath(["target"]) os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(targetDir)) peer = LocalPeer(name, collectDir) self.assertRaises(ValueError, peer.stagePeer, targetDir=targetDir) @unittest.skipIf(platformWindows(), "Behavior differs on Windows") def testStagePeer_005(self): """ Attempt to stage files with non-writable target directory. """ if not isRunningAsRoot(): # root doesn't get this error self.extractTar("tree1") name = "peer1" collectDir = self.buildPath(["tree1"]) targetDir = self.buildPath(["target"]) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) os.chmod(targetDir, 0o500) # read-only for user peer = LocalPeer(name, collectDir) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) os.chmod(targetDir, 0o777) # so we can remove it safely self.assertEqual(0, len(os.listdir(targetDir))) def testStagePeer_006(self): """ Attempt to stage files with empty collect directory. *Note:* This test assumes that scp returns an error if the directory is empty. """ self.extractTar("tree2") name = "peer1" collectDir = self.buildPath(["tree2", "dir001"]) targetDir = self.buildPath(["target"]) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = LocalPeer(name, collectDir) self.assertRaises(IOError, peer.stagePeer, targetDir=targetDir) stagedFiles = os.listdir(targetDir) self.assertEqual([], stagedFiles) @unittest.skipIf(platformWindows(), "Behavior differs on Windows") def testStagePeer_007(self): """ Attempt to stage files with empty collect directory, where the target directory name contains spaces. """ self.extractTar("tree2") name = "peer1" collectDir = self.buildPath(["tree2", "dir001"]) targetDir = self.buildPath([" target directory "]) # windows doesn't like paths that start or end with a space os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = LocalPeer(name, collectDir) self.assertRaises(IOError, peer.stagePeer, targetDir=targetDir) stagedFiles = os.listdir(targetDir) self.assertEqual([], stagedFiles) def testStagePeer_008(self): """ Attempt to stage files with non-empty collect directory. """ self.extractTar("tree1") name = "peer1" collectDir = self.buildPath(["tree1"]) targetDir = self.buildPath(["target"]) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) self.assertEqual(0, len(os.listdir(targetDir))) peer = LocalPeer(name, collectDir) count = peer.stagePeer(targetDir=targetDir) self.assertEqual(7, count) stagedFiles = os.listdir(targetDir) self.assertEqual(7, len(stagedFiles)) self.assertTrue("file001" in stagedFiles) self.assertTrue("file002" in stagedFiles) self.assertTrue("file003" in stagedFiles) self.assertTrue("file004" in stagedFiles) self.assertTrue("file005" in stagedFiles) self.assertTrue("file006" in stagedFiles) self.assertTrue("file007" in stagedFiles) def testStagePeer_009(self): """ Attempt to stage files with non-empty collect directory, where the target directory name contains spaces. """ self.extractTar("tree1") name = "peer1" collectDir = self.buildPath(["tree1"]) targetDir = self.buildPath(["target directory place"]) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) self.assertEqual(0, len(os.listdir(targetDir))) peer = LocalPeer(name, collectDir) count = peer.stagePeer(targetDir=targetDir) self.assertEqual(7, count) stagedFiles = os.listdir(targetDir) self.assertEqual(7, len(stagedFiles)) self.assertTrue("file001" in stagedFiles) self.assertTrue("file002" in stagedFiles) self.assertTrue("file003" in stagedFiles) self.assertTrue("file004" in stagedFiles) self.assertTrue("file005" in stagedFiles) self.assertTrue("file006" in stagedFiles) self.assertTrue("file007" in stagedFiles) def testStagePeer_010(self): """ Attempt to stage files with non-empty collect directory containing links and directories. """ self.extractTar("tree9") name = "peer1" collectDir = self.buildPath(["tree9"]) targetDir = self.buildPath(["target"]) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) self.assertEqual(0, len(os.listdir(targetDir))) peer = LocalPeer(name, collectDir) self.assertRaises(ValueError, peer.stagePeer, targetDir=targetDir) @unittest.skipIf(platformWindows(), "Behavior differs on Windows") def testStagePeer_011(self): """ Attempt to stage files with non-empty collect directory and attempt to set valid permissions. """ self.extractTar("tree1") name = "peer1" collectDir = self.buildPath(["tree1"]) targetDir = self.buildPath(["target"]) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) self.assertEqual(0, len(os.listdir(targetDir))) peer = LocalPeer(name, collectDir) if getMaskAsMode() == 0o400: permissions = 0o642 # arbitrary, but different than umask would give else: permissions = 0o400 # arbitrary count = peer.stagePeer(targetDir=targetDir, permissions=permissions) self.assertEqual(7, count) stagedFiles = os.listdir(targetDir) self.assertEqual(7, len(stagedFiles)) self.assertTrue("file001" in stagedFiles) self.assertTrue("file002" in stagedFiles) self.assertTrue("file003" in stagedFiles) self.assertTrue("file004" in stagedFiles) self.assertTrue("file005" in stagedFiles) self.assertTrue("file006" in stagedFiles) self.assertTrue("file007" in stagedFiles) self.assertEqual(permissions, self.getFileMode(["target", "file001"])) self.assertEqual(permissions, self.getFileMode(["target", "file002"])) self.assertEqual(permissions, self.getFileMode(["target", "file003"])) self.assertEqual(permissions, self.getFileMode(["target", "file004"])) self.assertEqual(permissions, self.getFileMode(["target", "file005"])) self.assertEqual(permissions, self.getFileMode(["target", "file006"])) self.assertEqual(permissions, self.getFileMode(["target", "file007"])) ###################### # TestRemotePeer class ###################### class TestRemotePeer(unittest.TestCase): """Tests for the RemotePeer class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() # Full tests require configured SSH connectivity, so I want to use my own # scp/ssh scripts that are set up to use an SSH agent. This means other # people won't be able to run the full tests... but that's always been # true, since I don't document the system setup needed to make them work. if runAllTests(): from CedarBackup3.util import PathResolverSingleton mapping = {} mapping["/usr/bin/ssh"] = pathJoin(os.environ["HOME"], "util", "ssh") mapping["/usr/bin/scp"] = pathJoin(os.environ["HOME"], "util", "scp") singleton = PathResolverSingleton.getInstance() singleton.fill(mapping) def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) def getFileMode(self, components): """Calls buildPath on components and then returns file mode for the file.""" return stat.S_IMODE(os.stat(self.buildPath(components)).st_mode) def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Tests basic functionality ############################ def testBasic_001(self): """ Make sure exception is thrown for non-absolute collect or working directory. """ name = REMOTE_HOST collectDir = "whatever/something/else/not/absolute" workingDir = "/tmp" remoteUser = getLogin() self.assertRaises(ValueError, RemotePeer, name, collectDir, workingDir, remoteUser) name = REMOTE_HOST collectDir = "/whatever/something/else/not/absolute" workingDir = "tmp" remoteUser = getLogin() self.assertRaises(ValueError, RemotePeer, name, collectDir, workingDir, remoteUser) def testBasic_002(self): """ Make sure attributes are set properly for valid constructor input. """ name = REMOTE_HOST collectDir = "/absolute/path/name" workingDir = "/tmp" remoteUser = getLogin() peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertEqual(name, peer.name) self.assertEqual(collectDir, peer.collectDir) self.assertEqual(workingDir, peer.workingDir) self.assertEqual(remoteUser, peer.remoteUser) self.assertEqual(None, peer.localUser) self.assertEqual(None, peer.rcpCommand) self.assertEqual(None, peer.rshCommand) self.assertEqual(None, peer.cbackCommand) self.assertEqual(DEF_RCP_COMMAND, peer._rcpCommandList) self.assertEqual(DEF_RSH_COMMAND, peer._rshCommandList) self.assertEqual(None, peer.ignoreFailureMode) def testBasic_003(self): """ Make sure attributes are set properly for valid constructor input, where the collect directory contains spaces. """ name = REMOTE_HOST collectDir = "/absolute/path/to/ a large directory" workingDir = "/tmp" remoteUser = getLogin() peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertEqual(name, peer.name) self.assertEqual(collectDir, peer.collectDir) self.assertEqual(workingDir, peer.workingDir) self.assertEqual(remoteUser, peer.remoteUser) self.assertEqual(None, peer.localUser) self.assertEqual(None, peer.rcpCommand) self.assertEqual(None, peer.rshCommand) self.assertEqual(None, peer.cbackCommand) self.assertEqual(DEF_RCP_COMMAND, peer._rcpCommandList) self.assertEqual(DEF_RSH_COMMAND, peer._rshCommandList) def testBasic_004(self): """ Make sure attributes are set properly for valid constructor input, custom rcp command. """ name = REMOTE_HOST collectDir = "/absolute/path/name" workingDir = "/tmp" remoteUser = getLogin() rcpCommand = "rcp -one --two three \"four five\" 'six seven' eight" peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand) self.assertEqual(name, peer.name) self.assertEqual(collectDir, peer.collectDir) self.assertEqual(workingDir, peer.workingDir) self.assertEqual(remoteUser, peer.remoteUser) self.assertEqual(None, peer.localUser) self.assertEqual(rcpCommand, peer.rcpCommand) self.assertEqual(None, peer.rshCommand) self.assertEqual(None, peer.cbackCommand) self.assertEqual(["rcp", "-one", "--two", "three", "four five", "'six", "seven'", "eight"], peer._rcpCommandList) self.assertEqual(DEF_RSH_COMMAND, peer._rshCommandList) def testBasic_005(self): """ Make sure attributes are set properly for valid constructor input, custom local user command. """ name = REMOTE_HOST collectDir = "/absolute/path/to/ a large directory" workingDir = "/tmp" remoteUser = getLogin() localUser = "pronovic" peer = RemotePeer(name, collectDir, workingDir, remoteUser, localUser=localUser) self.assertEqual(name, peer.name) self.assertEqual(collectDir, peer.collectDir) self.assertEqual(workingDir, peer.workingDir) self.assertEqual(remoteUser, peer.remoteUser) self.assertEqual(localUser, peer.localUser) self.assertEqual(None, peer.rcpCommand) self.assertEqual(DEF_RCP_COMMAND, peer._rcpCommandList) self.assertEqual(DEF_RSH_COMMAND, peer._rshCommandList) def testBasic_006(self): """ Make sure attributes are set properly for valid constructor input, custom rsh command. """ name = REMOTE_HOST remoteUser = getLogin() rshCommand = 'rsh --whatever -something "a b" else' peer = RemotePeer(name, remoteUser=remoteUser, rshCommand=rshCommand) self.assertEqual(name, peer.name) self.assertEqual(None, peer.collectDir) self.assertEqual(None, peer.workingDir) self.assertEqual(remoteUser, peer.remoteUser) self.assertEqual(None, peer.localUser) self.assertEqual(None, peer.rcpCommand) self.assertEqual(rshCommand, peer.rshCommand) self.assertEqual(None, peer.cbackCommand) self.assertEqual(DEF_RCP_COMMAND, peer._rcpCommandList) self.assertEqual(DEF_RCP_COMMAND, peer._rcpCommandList) self.assertEqual(["rsh", "--whatever", "-something", "a b", "else"], peer._rshCommandList) def testBasic_007(self): """ Make sure attributes are set properly for valid constructor input, custom cback command. """ name = REMOTE_HOST remoteUser = getLogin() cbackCommand = "cback --config=whatever --logfile=whatever --mode=064" peer = RemotePeer(name, remoteUser=remoteUser, cbackCommand=cbackCommand) self.assertEqual(name, peer.name) self.assertEqual(None, peer.collectDir) self.assertEqual(None, peer.workingDir) self.assertEqual(remoteUser, peer.remoteUser) self.assertEqual(None, peer.localUser) self.assertEqual(None, peer.rcpCommand) self.assertEqual(None, peer.rshCommand) self.assertEqual(cbackCommand, peer.cbackCommand) def testBasic_008(self): """ Make sure assignment works for all valid failure modes. """ peer = RemotePeer(name="name", remoteUser="user", ignoreFailureMode="all") self.assertEqual("all", peer.ignoreFailureMode) peer.ignoreFailureMode = "none" self.assertEqual("none", peer.ignoreFailureMode) peer.ignoreFailureMode = "daily" self.assertEqual("daily", peer.ignoreFailureMode) peer.ignoreFailureMode = "weekly" self.assertEqual("weekly", peer.ignoreFailureMode) self.failUnlessAssignRaises(ValueError, peer, "ignoreFailureMode", "bogus") ############################### # Test checkCollectIndicator() ############################### @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_001(self): """ Attempt to check collect indicator with invalid hostname. """ name = NONEXISTENT_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) remoteUser = getLogin() peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand=SAFE_RCP_COMMAND, rshCommand=SAFE_RSH_COMMAND) result = peer.checkCollectIndicator() self.assertEqual(False, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_002(self): """ Attempt to check collect indicator with invalid remote user. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" remoteUser = NONEXISTENT_USER os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand=SAFE_RCP_COMMAND, rshCommand=SAFE_RSH_COMMAND) result = peer.checkCollectIndicator() self.assertEqual(False, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_003(self): """ Attempt to check collect indicator with invalid rcp command. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" remoteUser = getLogin() rcpCommand = NONEXISTENT_CMD os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand) result = peer.checkCollectIndicator() self.assertEqual(False, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_004(self): """ Attempt to check collect indicator with non-existent collect directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" remoteUser = getLogin() self.assertTrue(not os.path.exists(collectDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator() self.assertEqual(False, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_005(self): """ Attempt to check collect indicator with non-readable collect directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) os.chmod(collectDir, 0o200) # user can't read his own directory peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator() self.assertEqual(False, result) os.chmod(collectDir, 0o777) # so we can remove it safely @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_006(self): """ Attempt to check collect indicator collect indicator file that does not exist. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" collectIndicator = self.buildPath(["collect", DEF_COLLECT_INDICATOR]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(collectIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator() self.assertEqual(False, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_007(self): """ Attempt to check collect indicator collect indicator file that does not exist, custom name. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" collectIndicator = self.buildPath(["collect", NONEXISTENT_FILE]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(collectIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator() self.assertEqual(False, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_008(self): """ Attempt to check collect indicator collect indicator file that does not exist, where the collect directory contains spaces. """ name = REMOTE_HOST collectDir = self.buildPath(["collect directory path"]) workingDir = "/tmp" collectIndicator = self.buildPath(["collect directory path", DEF_COLLECT_INDICATOR]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(collectIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator() self.assertEqual(False, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_009(self): """ Attempt to check collect indicator collect indicator file that does not exist, custom name, where the collect directory contains spaces. """ name = REMOTE_HOST collectDir = self.buildPath([" you collect here "]) workingDir = "/tmp" collectIndicator = self.buildPath([" you collect here ", NONEXISTENT_FILE]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(collectIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator() self.assertEqual(False, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_010(self): """ Attempt to check collect indicator collect indicator file that does exist. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" collectIndicator = self.buildPath(["collect", DEF_COLLECT_INDICATOR]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) with open(collectIndicator, "w") as f: f.write("") # touch the file self.assertTrue(os.path.exists(collectIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator() self.assertEqual(True, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_011(self): """ Attempt to check collect indicator collect indicator file that does exist, custom name. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" collectIndicator = self.buildPath(["collect", "whatever"]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) with open(collectIndicator, "w") as f: f.write("") # touch the file self.assertTrue(os.path.exists(collectIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator(collectIndicator="whatever") self.assertEqual(True, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_012(self): """ Attempt to check collect indicator collect indicator file that does exist, where the collect directory contains spaces. """ name = REMOTE_HOST collectDir = self.buildPath(["collect NOT"]) workingDir = "/tmp" collectIndicator = self.buildPath(["collect NOT", DEF_COLLECT_INDICATOR]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) with open(collectIndicator, "w") as f: f.write("") # touch the file self.assertTrue(os.path.exists(collectIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator() self.assertEqual(True, result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testCheckCollectIndicator_013(self): """ Attempt to check collect indicator collect indicator file that does exist, custom name, where the collect directory and indicator file contain spaces. """ name = REMOTE_HOST collectDir = self.buildPath([" from here collect!"]) workingDir = "/tmp" collectIndicator = self.buildPath([" from here collect!", "whatever, dude"]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) with open(collectIndicator, "w") as f: f.write("") # touch the file self.assertTrue(os.path.exists(collectIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) result = peer.checkCollectIndicator(collectIndicator="whatever, dude") self.assertEqual(True, result) ############################# # Test writeStageIndicator() ############################# @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_001(self): """ Attempt to write stage indicator with invalid hostname. """ name = NONEXISTENT_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) remoteUser = getLogin() peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand=SAFE_RCP_COMMAND, rshCommand=SAFE_RSH_COMMAND) self.assertRaises((IOError, OSError), peer.writeStageIndicator) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_002(self): """ Attempt to write stage indicator with invalid remote user. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" remoteUser = NONEXISTENT_USER os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand=SAFE_RCP_COMMAND, rshCommand=SAFE_RSH_COMMAND) self.assertRaises((IOError, OSError), peer.writeStageIndicator) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_003(self): """ Attempt to write stage indicator with invalid rcp command. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" remoteUser = getLogin() rcpCommand = NONEXISTENT_CMD os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand) self.assertRaises((IOError, OSError), peer.writeStageIndicator) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_004(self): """ Attempt to write stage indicator with non-existent collect directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" remoteUser = getLogin() self.assertTrue(not os.path.exists(collectDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises(IOError, peer.writeStageIndicator) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_005(self): """ Attempt to write stage indicator with non-writable collect directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" stageIndicator = self.buildPath(["collect", DEF_STAGE_INDICATOR]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(stageIndicator)) os.chmod(collectDir, 0o400) # read-only for user peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises((IOError, OSError), peer.writeStageIndicator) self.assertTrue(not os.path.exists(stageIndicator)) os.chmod(collectDir, 0o777) # so we can remove it safely @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_006(self): """ Attempt to write stage indicator in a valid directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" stageIndicator = self.buildPath(["collect", DEF_STAGE_INDICATOR]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(stageIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) peer.writeStageIndicator() self.assertTrue(os.path.exists(stageIndicator)) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_007(self): """ Attempt to write stage indicator in a valid directory, custom name. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" stageIndicator = self.buildPath(["collect", "newname"]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(stageIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) peer.writeStageIndicator(stageIndicator="newname") self.assertTrue(os.path.exists(stageIndicator)) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_008(self): """ Attempt to write stage indicator in a valid directory that contains spaces. """ name = REMOTE_HOST collectDir = self.buildPath(["with spaces collect"]) workingDir = "/tmp" stageIndicator = self.buildPath(["with spaces collect", DEF_STAGE_INDICATOR]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(stageIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand=SAFE_RCP_COMMAND, rshCommand=SAFE_RSH_COMMAND) peer.writeStageIndicator() self.assertTrue(os.path.exists(stageIndicator)) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteStageIndicator_009(self): """ Attempt to write stage indicator in a valid directory, custom name, where the collect directory and the custom name contain spaces. """ name = REMOTE_HOST collectDir = self.buildPath(["collect, soon"]) workingDir = "/tmp" stageIndicator = self.buildPath(["collect, soon", "new name with spaces"]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(stageIndicator)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand=SAFE_RCP_COMMAND, rshCommand=SAFE_RSH_COMMAND) peer.writeStageIndicator(stageIndicator="new name with spaces") self.assertTrue(os.path.exists(stageIndicator)) ################### # Test stagePeer() ################### @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_001(self): """ Attempt to stage files with invalid hostname. """ name = NONEXISTENT_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(collectDir) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand=SAFE_RCP_COMMAND, rshCommand=SAFE_RSH_COMMAND) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_002(self): """ Attempt to stage files with invalid remote user. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = NONEXISTENT_USER os.mkdir(collectDir) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand=SAFE_RCP_COMMAND, rshCommand=SAFE_RSH_COMMAND) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_003(self): """ Attempt to stage files with invalid rcp command. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() rcpCommand = NONEXISTENT_CMD os.mkdir(collectDir) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser, rcpCommand) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_004(self): """ Attempt to stage files with non-existent collect directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(targetDir) self.assertTrue(not os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_005(self): """ Attempt to stage files with non-readable collect directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(collectDir) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) os.chmod(collectDir, 0o200) # user can't read his own directory peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) os.chmod(collectDir, 0o777) # so we can remove it safely @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_006(self): """ Attempt to stage files with non-absolute target directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = "non/absolute/target" remoteUser = getLogin() self.assertTrue(not os.path.exists(collectDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises(ValueError, peer.stagePeer, targetDir=targetDir) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_007(self): """ Attempt to stage files with non-existent target directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(collectDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(not os.path.exists(targetDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises(ValueError, peer.stagePeer, targetDir=targetDir) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_008(self): """ Attempt to stage files with non-writable target directory. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(collectDir) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) os.chmod(targetDir, 0o400) # read-only for user peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) os.chmod(collectDir, 0o777) # so we can remove it safely self.assertEqual(0, len(os.listdir(targetDir))) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_009(self): """ Attempt to stage files with empty collect directory. *Note:* This test assumes that scp returns an error if the directory is empty. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(collectDir) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) stagedFiles = os.listdir(targetDir) self.assertEqual([], stagedFiles) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_010(self): """ Attempt to stage files with empty collect directory, with a target directory that contains spaces. *Note:* This test assumes that scp returns an error if the directory is empty. """ name = REMOTE_HOST collectDir = self.buildPath(["collect"]) workingDir = "/tmp" targetDir = self.buildPath(["target DIR"]) remoteUser = getLogin() os.mkdir(collectDir) os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) stagedFiles = os.listdir(targetDir) self.assertEqual([], stagedFiles) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_011(self): """ Attempt to stage files with non-empty collect directory. """ self.extractTar("tree1") name = REMOTE_HOST collectDir = self.buildPath(["tree1"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) self.assertEqual(0, len(os.listdir(targetDir))) peer = RemotePeer(name, collectDir, workingDir, remoteUser) count = peer.stagePeer(targetDir=targetDir) self.assertEqual(7, count) stagedFiles = os.listdir(targetDir) self.assertEqual(7, len(stagedFiles)) self.assertTrue("file001" in stagedFiles) self.assertTrue("file002" in stagedFiles) self.assertTrue("file003" in stagedFiles) self.assertTrue("file004" in stagedFiles) self.assertTrue("file005" in stagedFiles) self.assertTrue("file006" in stagedFiles) self.assertTrue("file007" in stagedFiles) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_012(self): """ Attempt to stage files with non-empty collect directory, with a target directory that contains spaces. """ self.extractTar("tree1") name = REMOTE_HOST collectDir = self.buildPath(["tree1"]) workingDir = "/tmp" targetDir = self.buildPath(["write the target here, now!"]) remoteUser = getLogin() os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) self.assertEqual(0, len(os.listdir(targetDir))) peer = RemotePeer(name, collectDir, workingDir, remoteUser) count = peer.stagePeer(targetDir=targetDir) self.assertEqual(7, count) stagedFiles = os.listdir(targetDir) self.assertEqual(7, len(stagedFiles)) self.assertTrue("file001" in stagedFiles) self.assertTrue("file002" in stagedFiles) self.assertTrue("file003" in stagedFiles) self.assertTrue("file004" in stagedFiles) self.assertTrue("file005" in stagedFiles) self.assertTrue("file006" in stagedFiles) self.assertTrue("file007" in stagedFiles) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_013(self): """ Attempt to stage files with non-empty collect directory containing links and directories. *Note:* We assume that scp copies the files even though it returns an error due to directories. """ self.extractTar("tree9") name = REMOTE_HOST collectDir = self.buildPath(["tree9"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) self.assertEqual(0, len(os.listdir(targetDir))) peer = RemotePeer(name, collectDir, workingDir, remoteUser) self.assertRaises((IOError, OSError), peer.stagePeer, targetDir=targetDir) stagedFiles = os.listdir(targetDir) self.assertEqual(2, len(stagedFiles)) self.assertTrue("file001" in stagedFiles) self.assertTrue("file002" in stagedFiles) @unittest.skipUnless(runAllTests(), "Limited test suite") def testStagePeer_014(self): """ Attempt to stage files with non-empty collect directory and attempt to set valid permissions. """ self.extractTar("tree1") name = REMOTE_HOST collectDir = self.buildPath(["tree1"]) workingDir = "/tmp" targetDir = self.buildPath(["target"]) remoteUser = getLogin() os.mkdir(targetDir) self.assertTrue(os.path.exists(collectDir)) self.assertTrue(os.path.exists(targetDir)) self.assertEqual(0, len(os.listdir(targetDir))) peer = RemotePeer(name, collectDir, workingDir, remoteUser) if getMaskAsMode() == 0o400: permissions = 0o642 # arbitrary, but different than umask would give else: permissions = 0o400 # arbitrary count = peer.stagePeer(targetDir=targetDir, permissions=permissions) self.assertEqual(7, count) stagedFiles = os.listdir(targetDir) self.assertEqual(7, len(stagedFiles)) self.assertTrue("file001" in stagedFiles) self.assertTrue("file002" in stagedFiles) self.assertTrue("file003" in stagedFiles) self.assertTrue("file004" in stagedFiles) self.assertTrue("file005" in stagedFiles) self.assertTrue("file006" in stagedFiles) self.assertTrue("file007" in stagedFiles) self.assertEqual(permissions, self.getFileMode(["target", "file001"])) self.assertEqual(permissions, self.getFileMode(["target", "file002"])) self.assertEqual(permissions, self.getFileMode(["target", "file003"])) self.assertEqual(permissions, self.getFileMode(["target", "file004"])) self.assertEqual(permissions, self.getFileMode(["target", "file005"])) self.assertEqual(permissions, self.getFileMode(["target", "file006"])) self.assertEqual(permissions, self.getFileMode(["target", "file007"])) ############################## # Test executeRemoteCommand() ############################## @unittest.skipUnless(runAllTests(), "Limited test suite") def testExecuteRemoteCommand(self): """ Test that a simple remote command succeeds. """ target = self.buildPath(["test.txt"]) name = REMOTE_HOST remoteUser = getLogin() command = "touch %s" % target self.assertFalse(os.path.exists(target)) peer = RemotePeer(name=name, remoteUser=remoteUser) peer.executeRemoteCommand(command) self.assertTrue(os.path.exists(target)) ############################ # Test _buildCbackCommand() ############################ @unittest.skipUnless(runAllTests(), "Limited test suite") def testBuildCbackCommand_001(self): """ Test with None for cbackCommand and action, False for fullBackup. """ self.assertRaises(ValueError, RemotePeer._buildCbackCommand, None, None, False) @unittest.skipUnless(runAllTests(), "Limited test suite") def testBuildCbackCommand_002(self): """ Test with None for cbackCommand, "collect" for action, False for fullBackup. """ result = RemotePeer._buildCbackCommand(None, "collect", False) self.assertEqual("/usr/bin/cback3 collect", result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testBuildCbackCommand_003(self): """ Test with "cback" for cbackCommand, "collect" for action, False for fullBackup. """ result = RemotePeer._buildCbackCommand("cback", "collect", False) self.assertEqual("cback collect", result) @unittest.skipUnless(runAllTests(), "Limited test suite") def testBuildCbackCommand_004(self): """ Test with "cback" for cbackCommand, "collect" for action, True for fullBackup. """ result = RemotePeer._buildCbackCommand("cback", "collect", True) self.assertEqual("cback --full collect", result) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1649563 cedar_backup3-3.8.1/tests/test_postgresql.py0000644000000000000000000011436114567004737016140 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2006,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests PostgreSQL extension functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/extend/postgresql.py. Code Coverage ============= This module contains individual tests for the many of the public functions and classes implemented in extend/postgresql.py. There are also tests for several of the private methods. Unfortunately, it's rather difficult to test this code in an automated fashion, even if you have access to PostgreSQL, since the actual dump would need to have access to a real database. Because of this, there aren't any tests below that actually talk to a database. As a compromise, I test some of the private methods in the implementation. Normally, I don't like to test private methods, but in this case, testing the private methods will help give us some reasonable confidence in the code even if we can't talk to a database.. This isn't perfect, but it's better than nothing. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract a node, build some XML from it, and then feed that XML back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a POSTGRESQLTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.extend.postgresql import LocalConfig, PostgresqlConfig from CedarBackup3.testutil import configureLogging, failUnlessAssignRaises, findResources from CedarBackup3.xmlutil import createOutputDom, serializeDom ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "postgresql.conf.1", "postgresql.conf.2", "postgresql.conf.3", "postgresql.conf.4", "postgresql.conf.5", ] ####################################################################### # Test Case Classes ####################################################################### ############################# # TestPostgresqlConfig class ############################# class TestPostgresqlConfig(unittest.TestCase): """Tests for the PostgresqlConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = PostgresqlConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.user) self.assertEqual(None, postgresql.compressMode) self.assertEqual(False, postgresql.all) self.assertEqual(None, postgresql.databases) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values, databases=None. """ postgresql = PostgresqlConfig("user", "none", False, None) self.assertEqual("user", postgresql.user) self.assertEqual("none", postgresql.compressMode) self.assertEqual(False, postgresql.all) self.assertEqual(None, postgresql.databases) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values, no databases. """ postgresql = PostgresqlConfig("user", "none", True, []) self.assertEqual("user", postgresql.user) self.assertEqual("none", postgresql.compressMode) self.assertEqual(True, postgresql.all) self.assertEqual([], postgresql.databases) def testConstructor_004(self): """ Test constructor with all values filled in, with valid values, with one database. """ postgresql = PostgresqlConfig("user", "gzip", True, ["one"]) self.assertEqual("user", postgresql.user) self.assertEqual("gzip", postgresql.compressMode) self.assertEqual(True, postgresql.all) self.assertEqual(["one"], postgresql.databases) def testConstructor_005(self): """ Test constructor with all values filled in, with valid values, with multiple databases. """ postgresql = PostgresqlConfig("user", "bzip2", True, ["one", "two"]) self.assertEqual("user", postgresql.user) self.assertEqual("bzip2", postgresql.compressMode) self.assertEqual(True, postgresql.all) self.assertEqual(["one", "two"], postgresql.databases) def testConstructor_006(self): """ Test assignment of user attribute, None value. """ postgresql = PostgresqlConfig(user="user") self.assertEqual("user", postgresql.user) postgresql.user = None self.assertEqual(None, postgresql.user) def testConstructor_007(self): """ Test assignment of user attribute, valid value. """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.user) postgresql.user = "user" self.assertEqual("user", postgresql.user) def testConstructor_008(self): """ Test assignment of user attribute, invalid value (empty). """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.user) self.failUnlessAssignRaises(ValueError, postgresql, "user", "") self.assertEqual(None, postgresql.user) def testConstructor_009(self): """ Test assignment of compressMode attribute, None value. """ postgresql = PostgresqlConfig(compressMode="none") self.assertEqual("none", postgresql.compressMode) postgresql.compressMode = None self.assertEqual(None, postgresql.compressMode) def testConstructor_010(self): """ Test assignment of compressMode attribute, valid value. """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.compressMode) postgresql.compressMode = "none" self.assertEqual("none", postgresql.compressMode) postgresql.compressMode = "gzip" self.assertEqual("gzip", postgresql.compressMode) postgresql.compressMode = "bzip2" self.assertEqual("bzip2", postgresql.compressMode) def testConstructor_011(self): """ Test assignment of compressMode attribute, invalid value (empty). """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.compressMode) self.failUnlessAssignRaises(ValueError, postgresql, "compressMode", "") self.assertEqual(None, postgresql.compressMode) def testConstructor_012(self): """ Test assignment of compressMode attribute, invalid value (not in list). """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.compressMode) self.failUnlessAssignRaises(ValueError, postgresql, "compressMode", "bogus") self.assertEqual(None, postgresql.compressMode) def testConstructor_013(self): """ Test assignment of all attribute, None value. """ postgresql = PostgresqlConfig(all=True) self.assertEqual(True, postgresql.all) postgresql.all = None self.assertEqual(False, postgresql.all) def testConstructor_014(self): """ Test assignment of all attribute, valid value (real boolean). """ postgresql = PostgresqlConfig() self.assertEqual(False, postgresql.all) postgresql.all = True self.assertEqual(True, postgresql.all) postgresql.all = False self.assertEqual(False, postgresql.all) def testConstructor_015(self): """ Test assignment of all attribute, valid value (expression). """ postgresql = PostgresqlConfig() self.assertEqual(False, postgresql.all) postgresql.all = 0 self.assertEqual(False, postgresql.all) postgresql.all = [] self.assertEqual(False, postgresql.all) postgresql.all = None self.assertEqual(False, postgresql.all) postgresql.all = ["a"] self.assertEqual(True, postgresql.all) postgresql.all = 3 self.assertEqual(True, postgresql.all) def testConstructor_016(self): """ Test assignment of databases attribute, None value. """ postgresql = PostgresqlConfig(databases=[]) self.assertEqual([], postgresql.databases) postgresql.databases = None self.assertEqual(None, postgresql.databases) def testConstructor_017(self): """ Test assignment of databases attribute, [] value. """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.databases) postgresql.databases = [] self.assertEqual([], postgresql.databases) def testConstructor_018(self): """ Test assignment of databases attribute, single valid entry. """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.databases) postgresql.databases = [ "/whatever", ] self.assertEqual(["/whatever"], postgresql.databases) postgresql.databases.append("/stuff") self.assertEqual(["/whatever", "/stuff"], postgresql.databases) def testConstructor_019(self): """ Test assignment of databases attribute, multiple valid entries. """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.databases) postgresql.databases = [ "/whatever", "/stuff", ] self.assertEqual(["/whatever", "/stuff"], postgresql.databases) postgresql.databases.append("/etc/X11") self.assertEqual(["/whatever", "/stuff", "/etc/X11"], postgresql.databases) def testConstructor_020(self): """ Test assignment of databases attribute, single invalid entry (empty). """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.databases) self.failUnlessAssignRaises(ValueError, postgresql, "databases", [""]) self.assertEqual(None, postgresql.databases) def testConstructor_021(self): """ Test assignment of databases attribute, mixed valid and invalid entries. """ postgresql = PostgresqlConfig() self.assertEqual(None, postgresql.databases) self.failUnlessAssignRaises(ValueError, postgresql, "databases", ["good", "", "alsogood"]) self.assertEqual(None, postgresql.databases) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ postgresql1 = PostgresqlConfig() postgresql2 = PostgresqlConfig() self.assertEqual(postgresql1, postgresql2) self.assertTrue(postgresql1 == postgresql2) self.assertTrue(not postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(postgresql1 >= postgresql2) self.assertTrue(not postgresql1 != postgresql2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None, list None. """ postgresql1 = PostgresqlConfig("user", "gzip", True, None) postgresql2 = PostgresqlConfig("user", "gzip", True, None) self.assertEqual(postgresql1, postgresql2) self.assertTrue(postgresql1 == postgresql2) self.assertTrue(not postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(postgresql1 >= postgresql2) self.assertTrue(not postgresql1 != postgresql2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None, list empty. """ postgresql1 = PostgresqlConfig("user", "bzip2", True, []) postgresql2 = PostgresqlConfig("user", "bzip2", True, []) self.assertEqual(postgresql1, postgresql2) self.assertTrue(postgresql1 == postgresql2) self.assertTrue(not postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(postgresql1 >= postgresql2) self.assertTrue(not postgresql1 != postgresql2) def testComparison_004(self): """ Test comparison of two identical objects, all attributes non-None, list non-empty. """ postgresql1 = PostgresqlConfig("user", "none", True, ["whatever"]) postgresql2 = PostgresqlConfig("user", "none", True, ["whatever"]) self.assertEqual(postgresql1, postgresql2) self.assertTrue(postgresql1 == postgresql2) self.assertTrue(not postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(postgresql1 >= postgresql2) self.assertTrue(not postgresql1 != postgresql2) def testComparison_005(self): """ Test comparison of two differing objects, user differs (one None). """ postgresql1 = PostgresqlConfig() postgresql2 = PostgresqlConfig(user="user") self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_006(self): """ Test comparison of two differing objects, user differs. """ postgresql1 = PostgresqlConfig("user1", "gzip", True, ["whatever"]) postgresql2 = PostgresqlConfig("user2", "gzip", True, ["whatever"]) self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_007(self): """ Test comparison of two differing objects, compressMode differs (one None). """ postgresql1 = PostgresqlConfig() postgresql2 = PostgresqlConfig(compressMode="gzip") self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs. """ postgresql1 = PostgresqlConfig("user", "bzip2", True, ["whatever"]) postgresql2 = PostgresqlConfig("user", "gzip", True, ["whatever"]) self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_009(self): """ Test comparison of two differing objects, all differs (one None). """ postgresql1 = PostgresqlConfig() postgresql2 = PostgresqlConfig(all=True) self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_010(self): """ Test comparison of two differing objects, all differs. """ postgresql1 = PostgresqlConfig("user", "gzip", False, ["whatever"]) postgresql2 = PostgresqlConfig("user", "gzip", True, ["whatever"]) self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_011(self): """ Test comparison of two differing objects, databases differs (one None, one empty). """ postgresql1 = PostgresqlConfig() postgresql2 = PostgresqlConfig(databases=[]) self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_012(self): """ Test comparison of two differing objects, databases differs (one None, one not empty). """ postgresql1 = PostgresqlConfig() postgresql2 = PostgresqlConfig(databases=["whatever"]) self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_013(self): """ Test comparison of two differing objects, databases differs (one empty, one not empty). """ postgresql1 = PostgresqlConfig("user", "gzip", True, []) postgresql2 = PostgresqlConfig("user", "gzip", True, ["whatever"]) self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(postgresql1 < postgresql2) self.assertTrue(postgresql1 <= postgresql2) self.assertTrue(not postgresql1 > postgresql2) self.assertTrue(not postgresql1 >= postgresql2) self.assertTrue(postgresql1 != postgresql2) def testComparison_014(self): """ Test comparison of two differing objects, databases differs (both not empty). """ postgresql1 = PostgresqlConfig("user", "gzip", True, ["whatever"]) postgresql2 = PostgresqlConfig("user", "gzip", True, ["whatever", "bogus"]) self.assertNotEqual(postgresql1, postgresql2) self.assertTrue(not postgresql1 == postgresql2) self.assertTrue(not postgresql1 < postgresql2) # note: different than standard due to unsorted list self.assertTrue(not postgresql1 <= postgresql2) # note: different than standard due to unsorted list self.assertTrue(postgresql1 > postgresql2) # note: different than standard due to unsorted list self.assertTrue(postgresql1 >= postgresql2) # note: different than standard due to unsorted list self.assertTrue(postgresql1 != postgresql2) ######################## # TestLocalConfig class ######################## class TestLocalConfig(unittest.TestCase): """Tests for the LocalConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) def validateAddConfig(self, origConfig): """ Validates that document dumped from ``LocalConfig.addConfig`` results in identical object. We dump a document containing just the postgresql configuration, and then make sure that if we push that document back into the ``LocalConfig`` object, that the resulting object matches the original. The ``self.failUnlessEqual`` method is used for the validation, so if the method call returns normally, everything is OK. Args: origConfig: Original configuration """ (xmlDom, parentNode) = createOutputDom() origConfig.addConfig(xmlDom, parentNode) xmlData = serializeDom(xmlDom) newConfig = LocalConfig(xmlData=xmlData, validate=False) self.assertEqual(origConfig, newConfig) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalConfig() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = LocalConfig(validate=False) self.assertEqual(None, config.postgresql) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = LocalConfig(validate=True) self.assertEqual(None, config.postgresql) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["postgresql.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test assignment of postgresql attribute, None value. """ config = LocalConfig() config.postgresql = None self.assertEqual(None, config.postgresql) def testConstructor_005(self): """ Test assignment of postgresql attribute, valid value. """ config = LocalConfig() config.postgresql = PostgresqlConfig() self.assertEqual(PostgresqlConfig(), config.postgresql) def testConstructor_006(self): """ Test assignment of postgresql attribute, invalid value (not PostgresqlConfig). """ config = LocalConfig() self.failUnlessAssignRaises(ValueError, config, "postgresql", "STRING!") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = LocalConfig() config2 = LocalConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = LocalConfig() config1.postgresql = PostgresqlConfig() config2 = LocalConfig() config2.postgresql = PostgresqlConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, postgresql differs (one None). """ config1 = LocalConfig() config2 = LocalConfig() config2.postgresql = PostgresqlConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, postgresql differs. """ config1 = LocalConfig() config1.postgresql = PostgresqlConfig(user="one") config2 = LocalConfig() config2.postgresql = PostgresqlConfig(user="two") self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on a None postgresql section. """ config = LocalConfig() config.postgresql = None self.assertRaises(ValueError, config.validate) def testValidate_002(self): """ Test validate on an empty postgresql section. """ config = LocalConfig() config.postgresql = PostgresqlConfig() self.assertRaises(ValueError, config.validate) def testValidate_003(self): """ Test validate on a non-empty postgresql section, all=True, databases=None. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "gzip", True, None) config.validate() def testValidate_004(self): """ Test validate on a non-empty postgresql section, all=True, empty databases. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "none", True, []) config.validate() def testValidate_005(self): """ Test validate on a non-empty postgresql section, all=True, non-empty databases. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "bzip2", True, ["whatever"]) self.assertRaises(ValueError, config.validate) def testValidate_006(self): """ Test validate on a non-empty postgresql section, all=False, databases=None. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "gzip", False, None) self.assertRaises(ValueError, config.validate) def testValidate_007(self): """ Test validate on a non-empty postgresql section, all=False, empty databases. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "bzip2", False, []) self.assertRaises(ValueError, config.validate) def testValidate_008(self): """ Test validate on a non-empty postgresql section, all=False, non-empty databases. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "gzip", False, ["whatever"]) config.validate() def testValidate_009(self): """ Test validate on a non-empty postgresql section, with user=None. """ config = LocalConfig() config.postgresql = PostgresqlConfig(None, "gzip", True, None) config.validate() ############################ # Test parsing of documents ############################ def testParse_001(self): """ Parse empty config document. """ path = self.resources["postgresql.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlPath=path, validate=True) self.assertRaises(ValueError, LocalConfig, xmlData=contents, validate=True) config = LocalConfig(xmlPath=path, validate=False) self.assertEqual(None, config.postgresql) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual(None, config.postgresql) def testParse_003(self): """ Parse config document containing only a postgresql section, no databases, all=True. """ path = self.resources["postgresql.conf.2"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.postgresql) self.assertEqual("user", config.postgresql.user) self.assertEqual("none", config.postgresql.compressMode) self.assertEqual(True, config.postgresql.all) self.assertEqual(None, config.postgresql.databases) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual("user", config.postgresql.user) self.assertEqual("none", config.postgresql.compressMode) self.assertEqual(True, config.postgresql.all) self.assertEqual(None, config.postgresql.databases) def testParse_004(self): """ Parse config document containing only a postgresql section, single database, all=False. """ path = self.resources["postgresql.conf.3"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.postgresql) self.assertEqual("user", config.postgresql.user) self.assertEqual("gzip", config.postgresql.compressMode) self.assertEqual(False, config.postgresql.all) self.assertEqual(["database"], config.postgresql.databases) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.postgresql) self.assertEqual("user", config.postgresql.user) self.assertEqual("gzip", config.postgresql.compressMode) self.assertEqual(False, config.postgresql.all) self.assertEqual(["database"], config.postgresql.databases) def testParse_005(self): """ Parse config document containing only a postgresql section, multiple databases, all=False. """ path = self.resources["postgresql.conf.4"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.postgresql) self.assertEqual("user", config.postgresql.user) self.assertEqual("bzip2", config.postgresql.compressMode) self.assertEqual(False, config.postgresql.all) self.assertEqual(["database1", "database2"], config.postgresql.databases) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.postgresql) self.assertEqual("user", config.postgresql.user) self.assertEqual("bzip2", config.postgresql.compressMode) self.assertEqual(False, config.postgresql.all) self.assertEqual(["database1", "database2"], config.postgresql.databases) def testParse_006(self): """ Parse config document containing only a postgresql section, no user, multiple databases, all=False. """ path = self.resources["postgresql.conf.5"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.postgresql) self.assertEqual(None, config.postgresql.user) self.assertEqual("bzip2", config.postgresql.compressMode) self.assertEqual(False, config.postgresql.all) self.assertEqual(["database1", "database2"], config.postgresql.databases) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.postgresql) self.assertEqual(None, config.postgresql.user) self.assertEqual("bzip2", config.postgresql.compressMode) self.assertEqual(False, config.postgresql.all) self.assertEqual(["database1", "database2"], config.postgresql.databases) ################### # Test addConfig() ################### def testAddConfig_001(self): """ Test with empty config document """ config = LocalConfig() self.validateAddConfig(config) def testAddConfig_003(self): """ Test with no databases, all other values filled in, all=True. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "none", True, None) self.validateAddConfig(config) def testAddConfig_004(self): """ Test with no databases, all other values filled in, all=False. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "gzip", False, None) self.validateAddConfig(config) def testAddConfig_005(self): """ Test with single database, all other values filled in, all=True. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "bzip2", True, ["database"]) self.validateAddConfig(config) def testAddConfig_006(self): """ Test with single database, all other values filled in, all=False. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "none", False, ["database"]) self.validateAddConfig(config) def testAddConfig_007(self): """ Test with multiple databases, all other values filled in, all=True. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "bzip2", True, ["database1", "database2"]) self.validateAddConfig(config) def testAddConfig_008(self): """ Test with multiple databases, all other values filled in, all=False. """ config = LocalConfig() config.postgresql = PostgresqlConfig("user", "gzip", True, ["database1", "database2"]) self.validateAddConfig(config) def testAddConfig_009(self): """ Test with multiple databases, user=None but all other values filled in, all=False. """ config = LocalConfig() config.postgresql = PostgresqlConfig(None, "gzip", True, ["database1", "database2"]) self.validateAddConfig(config) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1649563 cedar_backup3-3.8.1/tests/test_span.py0000644000000000000000000001144714567004737014677 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests span tool functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/tools/span.py. Code Coverage ============= This module contains individual tests for the many of the public functions and classes implemented in tools/span.py. Where possible, we test functions that print output by passing a custom file descriptor. Sometimes, we only ensure that a function or method runs without failure, and we don't validate what its result is or what it prints out. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a SPANTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.testutil import captureOutput, configureLogging from CedarBackup3.tools.span import Options, _usage, _version ####################################################################### # Test Case Classes ####################################################################### ###################### # TestFunctions class ###################### class TestFunctions(unittest.TestCase): """Tests for the public functions.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ######################## # Test simple functions ######################## def testSimpleFuncs_001(self): """ Test that the _usage() function runs without errors. We don't care what the output is, and we don't check. """ captureOutput(_usage) def testSimpleFuncs_002(self): """ Test that the _version() function runs without errors. We don't care what the output is, and we don't check. """ captureOutput(_version) ######################## # TestSpanOptions class ######################## class TestSpanOptions(unittest.TestCase): """Tests for the SpanOptions class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = Options() obj.__repr__() obj.__str__() ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1649563 cedar_backup3-3.8.1/tests/test_split.py0000644000000000000000000013302214567004737015063 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests split extension functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/extend/split.py. Code Coverage ============= This module contains individual tests for the the public classes implemented in extend/split.py. There are also tests for some of the private functions. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract a node, build some XML from it, and then feed that XML back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. Full vs. Reduced Tests ====================== Some Cedar Backup regression tests require a specialized environment in order to run successfully. This environment won't necessarily be available on every build system out there (for instance, on a Debian autobuilder). Because of this, the default behavior is to run a "reduced feature set" test suite that has no surprising system, kernel or network requirements. If you want to run all of the tests, set SPLITTESTS_FULL to "Y" in the environment. In this module, the primary dependency is that the split utility must be available. There is also one test that wants at least one non-English locale (fr_FR, ru_RU or pt_PT) available to check localization issues (but that test will just automatically be skipped if such a locale is not available). @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import os import tempfile import unittest from CedarBackup3.extend.split import ByteQuantity, LocalConfig, SplitConfig, _splitDailyDir, _splitFile from CedarBackup3.testutil import ( availableLocales, buildPath, configureLogging, extractTar, failUnlessAssignRaises, findResources, removedir, ) from CedarBackup3.util import UNIT_BYTES, UNIT_GBYTES, UNIT_KBYTES, UNIT_MBYTES, pathJoin from CedarBackup3.xmlutil import createOutputDom, serializeDom ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "split.conf.1", "split.conf.2", "split.conf.3", "split.conf.4", "split.conf.5", "tree21.tar.gz", ] INVALID_PATH = "bogus" # This path name should never exist ####################################################################### # Utility functions ####################################################################### def runAllTests(): """Returns true/false depending on whether the full test suite should be run.""" if "SPLITTESTS_FULL" in os.environ: return os.environ["SPLITTESTS_FULL"] == "Y" else: return False ####################################################################### # Test Case Classes ####################################################################### ########################## # TestSplitConfig class ########################## class TestSplitConfig(unittest.TestCase): """Tests for the SplitConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = SplitConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ split = SplitConfig() self.assertEqual(None, split.sizeLimit) self.assertEqual(None, split.splitSize) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values. """ split = SplitConfig(ByteQuantity("1.0", UNIT_BYTES), ByteQuantity("2.0", UNIT_KBYTES)) self.assertEqual(ByteQuantity("1.0", UNIT_BYTES), split.sizeLimit) self.assertEqual(ByteQuantity("2.0", UNIT_KBYTES), split.splitSize) def testConstructor_003(self): """ Test assignment of sizeLimit attribute, None value. """ split = SplitConfig(sizeLimit=ByteQuantity("1.0", UNIT_BYTES)) self.assertEqual(ByteQuantity("1.0", UNIT_BYTES), split.sizeLimit) split.sizeLimit = None self.assertEqual(None, split.sizeLimit) def testConstructor_004(self): """ Test assignment of sizeLimit attribute, valid value. """ split = SplitConfig() self.assertEqual(None, split.sizeLimit) split.sizeLimit = ByteQuantity("1.0", UNIT_BYTES) self.assertEqual(ByteQuantity("1.0", UNIT_BYTES), split.sizeLimit) def testConstructor_005(self): """ Test assignment of sizeLimit attribute, invalid value (empty). """ split = SplitConfig() self.assertEqual(None, split.sizeLimit) self.failUnlessAssignRaises(ValueError, split, "sizeLimit", "") self.assertEqual(None, split.sizeLimit) def testConstructor_006(self): """ Test assignment of sizeLimit attribute, invalid value (not a ByteQuantity). """ split = SplitConfig() self.assertEqual(None, split.sizeLimit) self.failUnlessAssignRaises(ValueError, split, "sizeLimit", "1.0 GB") self.assertEqual(None, split.sizeLimit) def testConstructor_007(self): """ Test assignment of splitSize attribute, None value. """ split = SplitConfig(splitSize=ByteQuantity("1.00", UNIT_KBYTES)) self.assertEqual(ByteQuantity("1.00", UNIT_KBYTES), split.splitSize) split.splitSize = None self.assertEqual(None, split.splitSize) def testConstructor_008(self): """ Test assignment of splitSize attribute, valid value. """ split = SplitConfig() self.assertEqual(None, split.splitSize) split.splitSize = ByteQuantity("1.00", UNIT_KBYTES) self.assertEqual(ByteQuantity("1.00", UNIT_KBYTES), split.splitSize) def testConstructor_009(self): """ Test assignment of splitSize attribute, invalid value (empty). """ split = SplitConfig() self.assertEqual(None, split.splitSize) self.failUnlessAssignRaises(ValueError, split, "splitSize", "") self.assertEqual(None, split.splitSize) def testConstructor_010(self): """ Test assignment of splitSize attribute, invalid value (not a ByteQuantity). """ split = SplitConfig() self.assertEqual(None, split.splitSize) self.failUnlessAssignRaises(ValueError, split, "splitSize", 12) self.assertEqual(None, split.splitSize) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ split1 = SplitConfig() split2 = SplitConfig() self.assertEqual(split1, split2) self.assertTrue(split1 == split2) self.assertTrue(not split1 < split2) self.assertTrue(split1 <= split2) self.assertTrue(not split1 > split2) self.assertTrue(split1 >= split2) self.assertTrue(not split1 != split2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ split1 = SplitConfig(ByteQuantity("99", UNIT_KBYTES), ByteQuantity("1.00", UNIT_MBYTES)) split2 = SplitConfig(ByteQuantity("99", UNIT_KBYTES), ByteQuantity("1.00", UNIT_MBYTES)) self.assertEqual(split1, split2) self.assertTrue(split1 == split2) self.assertTrue(not split1 < split2) self.assertTrue(split1 <= split2) self.assertTrue(not split1 > split2) self.assertTrue(split1 >= split2) self.assertTrue(not split1 != split2) def testComparison_003(self): """ Test comparison of two differing objects, sizeLimit differs (one None). """ split1 = SplitConfig() split2 = SplitConfig(sizeLimit=ByteQuantity("99", UNIT_KBYTES)) self.assertNotEqual(split1, split2) self.assertTrue(not split1 == split2) self.assertTrue(split1 < split2) self.assertTrue(split1 <= split2) self.assertTrue(not split1 > split2) self.assertTrue(not split1 >= split2) self.assertTrue(split1 != split2) def testComparison_004(self): """ Test comparison of two differing objects, sizeLimit differs. """ split1 = SplitConfig(ByteQuantity("99", UNIT_BYTES), ByteQuantity("1.00", UNIT_MBYTES)) split2 = SplitConfig(ByteQuantity("99", UNIT_KBYTES), ByteQuantity("1.00", UNIT_MBYTES)) self.assertNotEqual(split1, split2) self.assertTrue(not split1 == split2) self.assertTrue(split1 < split2) self.assertTrue(split1 <= split2) self.assertTrue(not split1 > split2) self.assertTrue(not split1 >= split2) self.assertTrue(split1 != split2) def testComparison_005(self): """ Test comparison of two differing objects, splitSize differs (one None). """ split1 = SplitConfig() split2 = SplitConfig(splitSize=ByteQuantity("1.00", UNIT_MBYTES)) self.assertNotEqual(split1, split2) self.assertTrue(not split1 == split2) self.assertTrue(split1 < split2) self.assertTrue(split1 <= split2) self.assertTrue(not split1 > split2) self.assertTrue(not split1 >= split2) self.assertTrue(split1 != split2) def testComparison_006(self): """ Test comparison of two differing objects, splitSize differs. """ split1 = SplitConfig(ByteQuantity("99", UNIT_KBYTES), ByteQuantity("0.5", UNIT_MBYTES)) split2 = SplitConfig(ByteQuantity("99", UNIT_KBYTES), ByteQuantity("1.00", UNIT_MBYTES)) self.assertNotEqual(split1, split2) self.assertTrue(not split1 == split2) self.assertTrue(split1 < split2) self.assertTrue(split1 <= split2) self.assertTrue(not split1 > split2) self.assertTrue(not split1 >= split2) self.assertTrue(split1 != split2) ######################## # TestLocalConfig class ######################## class TestLocalConfig(unittest.TestCase): """Tests for the LocalConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) def validateAddConfig(self, origConfig): """ Validates that document dumped from ``LocalConfig.addConfig`` results in identical object. We dump a document containing just the split configuration, and then make sure that if we push that document back into the ``LocalConfig`` object, that the resulting object matches the original. The ``self.failUnlessEqual`` method is used for the validation, so if the method call returns normally, everything is OK. Args: origConfig: Original configuration """ (xmlDom, parentNode) = createOutputDom() origConfig.addConfig(xmlDom, parentNode) xmlData = serializeDom(xmlDom) newConfig = LocalConfig(xmlData=xmlData, validate=False) self.assertEqual(origConfig, newConfig) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalConfig() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = LocalConfig(validate=False) self.assertEqual(None, config.split) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = LocalConfig(validate=True) self.assertEqual(None, config.split) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["split.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test assignment of split attribute, None value. """ config = LocalConfig() config.split = None self.assertEqual(None, config.split) def testConstructor_005(self): """ Test assignment of split attribute, valid value. """ config = LocalConfig() config.split = SplitConfig() self.assertEqual(SplitConfig(), config.split) def testConstructor_006(self): """ Test assignment of split attribute, invalid value (not SplitConfig). """ config = LocalConfig() self.failUnlessAssignRaises(ValueError, config, "split", "STRING!") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = LocalConfig() config2 = LocalConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = LocalConfig() config1.split = SplitConfig() config2 = LocalConfig() config2.split = SplitConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, split differs (one None). """ config1 = LocalConfig() config2 = LocalConfig() config2.split = SplitConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, split differs. """ config1 = LocalConfig() config1.split = SplitConfig(sizeLimit=ByteQuantity("0.1", UNIT_MBYTES)) config2 = LocalConfig() config2.split = SplitConfig(sizeLimit=ByteQuantity("1.00", UNIT_MBYTES)) self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on a None split section. """ config = LocalConfig() config.split = None self.assertRaises(ValueError, config.validate) def testValidate_002(self): """ Test validate on an empty split section. """ config = LocalConfig() config.split = SplitConfig() self.assertRaises(ValueError, config.validate) def testValidate_003(self): """ Test validate on a non-empty split section with no values filled in. """ config = LocalConfig() config.split = SplitConfig(None, None) self.assertRaises(ValueError, config.validate) def testValidate_004(self): """ Test validate on a non-empty split section with only one value filled in. """ config = LocalConfig() config.split = SplitConfig(ByteQuantity("1.00", UNIT_MBYTES), None) self.assertRaises(ValueError, config.validate) config.split = SplitConfig(None, ByteQuantity("1.00", UNIT_MBYTES)) self.assertRaises(ValueError, config.validate) def testValidate_005(self): """ Test validate on a non-empty split section with valid values filled in. """ config = LocalConfig() config.split = SplitConfig(ByteQuantity("1.00", UNIT_MBYTES), ByteQuantity("1.00", UNIT_MBYTES)) config.validate() ############################ # Test parsing of documents ############################ def testParse_001(self): """ Parse empty config document. """ path = self.resources["split.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlPath=path, validate=True) self.assertRaises(ValueError, LocalConfig, xmlData=contents, validate=True) config = LocalConfig(xmlPath=path, validate=False) self.assertEqual(None, config.split) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual(None, config.split) def testParse_002(self): """ Parse config document with filled-in values, size in bytes. """ path = self.resources["split.conf.2"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.split) self.assertEqual(ByteQuantity("12345", UNIT_BYTES), config.split.sizeLimit) self.assertEqual(ByteQuantity("67890.0", UNIT_BYTES), config.split.splitSize) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.split) self.assertEqual(ByteQuantity("12345", UNIT_BYTES), config.split.sizeLimit) self.assertEqual(ByteQuantity("67890.0", UNIT_BYTES), config.split.splitSize) def testParse_003(self): """ Parse config document with filled-in values, size in KB. """ path = self.resources["split.conf.3"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.split) self.assertEqual(ByteQuantity("1.25", UNIT_KBYTES), config.split.sizeLimit) self.assertEqual(ByteQuantity("0.6", UNIT_KBYTES), config.split.splitSize) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.split) self.assertEqual(ByteQuantity("1.25", UNIT_KBYTES), config.split.sizeLimit) self.assertEqual(ByteQuantity("0.6", UNIT_KBYTES), config.split.splitSize) def testParse_004(self): """ Parse config document with filled-in values, size in MB. """ path = self.resources["split.conf.4"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.split) self.assertEqual(ByteQuantity("1.25", UNIT_MBYTES), config.split.sizeLimit) self.assertEqual(ByteQuantity("0.6", UNIT_MBYTES), config.split.splitSize) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.split) self.assertEqual(ByteQuantity("1.25", UNIT_MBYTES), config.split.sizeLimit) self.assertEqual(ByteQuantity("0.6", UNIT_MBYTES), config.split.splitSize) def testParse_005(self): """ Parse config document with filled-in values, size in GB. """ path = self.resources["split.conf.5"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.split) self.assertEqual(ByteQuantity("1.25", UNIT_GBYTES), config.split.sizeLimit) self.assertEqual(ByteQuantity("0.6", UNIT_GBYTES), config.split.splitSize) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.split) self.assertEqual(ByteQuantity("1.25", UNIT_GBYTES), config.split.sizeLimit) self.assertEqual(ByteQuantity("0.6", UNIT_GBYTES), config.split.splitSize) ################### # Test addConfig() ################### def testAddConfig_001(self): """ Test with empty config document. """ split = SplitConfig() config = LocalConfig() config.split = split self.validateAddConfig(config) def testAddConfig_002(self): """ Test with values set, byte values. """ split = SplitConfig(ByteQuantity("57521.0", UNIT_BYTES), ByteQuantity("121231", UNIT_BYTES)) config = LocalConfig() config.split = split self.validateAddConfig(config) def testAddConfig_003(self): """ Test with values set, KB values. """ split = SplitConfig(ByteQuantity("12", UNIT_KBYTES), ByteQuantity("63352", UNIT_KBYTES)) config = LocalConfig() config.split = split self.validateAddConfig(config) def testAddConfig_004(self): """ Test with values set, MB values. """ split = SplitConfig(ByteQuantity("12", UNIT_MBYTES), ByteQuantity("63352", UNIT_MBYTES)) config = LocalConfig() config.split = split self.validateAddConfig(config) def testAddConfig_005(self): """ Test with values set, GB values. """ split = SplitConfig(ByteQuantity("12", UNIT_GBYTES), ByteQuantity("63352", UNIT_GBYTES)) config = LocalConfig() config.split = split self.validateAddConfig(config) ###################### # TestFunctions class ###################### @unittest.skipUnless(runAllTests(), "Limited test suite") class TestFunctions(unittest.TestCase): """Tests for the functions in split.py.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() @classmethod def checkDisabled(cls): if not runAllTests(): raise unittest.SkipTest("Disabled") def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): try: removedir(self.tmpdir) except: pass ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) def checkSplit(self, sourcePath, origSize, splitSize): """Checks that a file was split properly.""" wholeFiles = int(float(origSize) / float(splitSize)) leftoverBytes = int(float(origSize) % float(splitSize)) for i in range(0, wholeFiles): splitPath = "%s_%05d" % (sourcePath, i) self.assertTrue(os.path.exists(splitPath)) self.assertEqual(splitSize, os.stat(splitPath).st_size) if leftoverBytes > 0: splitPath = "%s_%05d" % (sourcePath, wholeFiles) self.assertTrue(os.path.exists(splitPath)) self.assertEqual(leftoverBytes, os.stat(splitPath).st_size) def findBadLocale(self): """ The split command localizes its output for certain locales. This breaks the parsing code in split.py. This method returns a list of the locales (if any) that are currently configured which could be expected to cause a failure if the localization-fixing code doesn't work. """ locales = availableLocales() if "fr_FR" in locales: return "fr_FR" if "pl_PL" in locales: return "pl_PL" if "ru_RU" in locales: return "ru_RU" return None #################### # Test _splitFile() #################### def testSplitFile_001(self): """ Test with a nonexistent file. """ self.extractTar("tree21") sourcePath = self.buildPath(["tree21", "2007", "01", "01", INVALID_PATH]) self.assertFalse(os.path.exists(sourcePath)) splitSize = ByteQuantity("320", UNIT_BYTES) self.assertRaises(ValueError, _splitFile, sourcePath, splitSize, None, None, removeSource=False) def testSplitFile_002(self): """ Test with integer split size, removeSource=False. """ self.extractTar("tree21") sourcePath = self.buildPath(["tree21", "2007", "01", "01", "system1", "file001.a.b"]) self.assertTrue(os.path.exists(sourcePath)) splitSize = ByteQuantity("320", UNIT_BYTES) _splitFile(sourcePath, splitSize, None, None, removeSource=False) self.assertTrue(os.path.exists(sourcePath)) self.checkSplit(sourcePath, 3200, 320) def testSplitFile_003(self): """ Test with floating point split size, removeSource=False. """ self.extractTar("tree21") sourcePath = self.buildPath(["tree21", "2007", "01", "01", "system1", "file001.a.b"]) self.assertTrue(os.path.exists(sourcePath)) splitSize = ByteQuantity("320.1", UNIT_BYTES) _splitFile(sourcePath, splitSize, None, None, removeSource=False) self.assertTrue(os.path.exists(sourcePath)) self.checkSplit(sourcePath, 3200, 320) def testSplitFile_004(self): """ Test with integer split size, removeSource=True. """ self.extractTar("tree21") sourcePath = self.buildPath(["tree21", "2007", "01", "01", "system1", "file001.a.b"]) self.assertTrue(os.path.exists(sourcePath)) splitSize = ByteQuantity("320", UNIT_BYTES) _splitFile(sourcePath, splitSize, None, None, removeSource=True) self.assertFalse(os.path.exists(sourcePath)) self.checkSplit(sourcePath, 3200, 320) def testSplitFile_005(self): """ Test with a local other than "C" or "en_US" set. """ locale = self.findBadLocale() if locale is not None: os.environ["LANG"] = locale os.environ["LC_ADDRESS"] = locale os.environ["LC_ALL"] = locale os.environ["LC_COLLATE"] = locale os.environ["LC_CTYPE"] = locale os.environ["LC_IDENTIFICATION"] = locale os.environ["LC_MEASUREMENT"] = locale os.environ["LC_MESSAGES"] = locale os.environ["LC_MONETARY"] = locale os.environ["LC_NAME"] = locale os.environ["LC_NUMERIC"] = locale os.environ["LC_PAPER"] = locale os.environ["LC_TELEPHONE"] = locale os.environ["LC_TIME"] = locale self.extractTar("tree21") sourcePath = self.buildPath(["tree21", "2007", "01", "01", "system1", "file001.a.b"]) self.assertTrue(os.path.exists(sourcePath)) splitSize = ByteQuantity("320", UNIT_BYTES) _splitFile(sourcePath, splitSize, None, None, removeSource=True) self.assertFalse(os.path.exists(sourcePath)) self.checkSplit(sourcePath, 3200, 320) ########################## # Test _splitDailyDir() ########################## def testSplitDailyDir_001(self): """ Test with a nonexistent daily staging directory. """ self.extractTar("tree21") dailyDir = self.buildPath(["tree21", "2007", "01", INVALID_PATH]) self.assertFalse(os.path.exists(dailyDir)) sizeLimit = ByteQuantity("1.0", UNIT_MBYTES) splitSize = ByteQuantity("100000", UNIT_BYTES) self.assertRaises(ValueError, _splitDailyDir, dailyDir, sizeLimit, splitSize, None, None) def testSplitDailyDir_002(self): """ Test with 1.0 MB limit. """ self.extractTar("tree21") dailyDir = self.buildPath(["tree21", "2007", "01", "01"]) self.assertTrue(os.path.exists(dailyDir) and os.path.isdir(dailyDir)) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) sizeLimit = ByteQuantity("1.0", UNIT_MBYTES) splitSize = ByteQuantity("100000", UNIT_BYTES) _splitDailyDir(dailyDir, sizeLimit, splitSize, None, None) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) def testSplitDailyDir_003(self): """ Test with 100,000 byte limit, chopped down to 10 KB """ self.extractTar("tree21") dailyDir = self.buildPath(["tree21", "2007", "01", "01"]) self.assertTrue(os.path.exists(dailyDir) and os.path.isdir(dailyDir)) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) sizeLimit = ByteQuantity("100000", UNIT_BYTES) splitSize = ByteQuantity("10", UNIT_KBYTES) _splitDailyDir(dailyDir, sizeLimit, splitSize, None, None) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) self.checkSplit(pathJoin(dailyDir, "system1", "file003"), 320000, 10 * 1024) self.checkSplit(pathJoin(dailyDir, "system3", "file003"), 100001, 10 * 1024) def testSplitDailyDir_004(self): """ Test with 99,999 byte limit, chopped down to 5,000 bytes """ self.extractTar("tree21") dailyDir = self.buildPath(["tree21", "2007", "01", "01"]) self.assertTrue(os.path.exists(dailyDir) and os.path.isdir(dailyDir)) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) sizeLimit = ByteQuantity("99999", UNIT_BYTES) splitSize = ByteQuantity("5000", UNIT_BYTES) _splitDailyDir(dailyDir, sizeLimit, splitSize, None, None) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) self.checkSplit(pathJoin(dailyDir, "system1", "file003"), 320000, 5000) self.checkSplit(pathJoin(dailyDir, "system2", "file003"), 100000, 5000) self.checkSplit(pathJoin(dailyDir, "system3", "file002"), 100000, 5000) self.checkSplit(pathJoin(dailyDir, "system3", "file003"), 100001, 5000) def testSplitDailyDir_005(self): """ Test with 99,998 byte limit, chopped down to 2500 bytes """ self.extractTar("tree21") dailyDir = self.buildPath(["tree21", "2007", "01", "01"]) self.assertTrue(os.path.exists(dailyDir) and os.path.isdir(dailyDir)) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) sizeLimit = ByteQuantity("10000.0", UNIT_BYTES) splitSize = ByteQuantity("2500", UNIT_BYTES) _splitDailyDir(dailyDir, sizeLimit, splitSize, None, None) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) self.checkSplit(pathJoin(dailyDir, "system1", "file002"), 32000, 2500) self.checkSplit(pathJoin(dailyDir, "system1", "file003"), 320000, 2500) self.checkSplit(pathJoin(dailyDir, "system2", "file003"), 100000, 2500) self.checkSplit(pathJoin(dailyDir, "system3", "file001"), 99999, 2500) self.checkSplit(pathJoin(dailyDir, "system3", "file002"), 100000, 2500) self.checkSplit(pathJoin(dailyDir, "system3", "file003"), 100001, 2500) def testSplitDailyDir_006(self): """ Test with 10,000 byte limit, chopped down to 1024 bytes """ self.extractTar("tree21") dailyDir = self.buildPath(["tree21", "2007", "01", "01"]) self.assertTrue(os.path.exists(dailyDir) and os.path.isdir(dailyDir)) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) sizeLimit = ByteQuantity("10000", UNIT_BYTES) splitSize = ByteQuantity("1.0", UNIT_KBYTES) _splitDailyDir(dailyDir, sizeLimit, splitSize, None, None) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) self.checkSplit(pathJoin(dailyDir, "system1", "file002"), 32000, 1 * 1024) self.checkSplit(pathJoin(dailyDir, "system1", "file003"), 320000, 1 * 1024) self.checkSplit(pathJoin(dailyDir, "system2", "file003"), 100000, 1 * 1024) self.checkSplit(pathJoin(dailyDir, "system3", "file001"), 99999, 1 * 1024) self.checkSplit(pathJoin(dailyDir, "system3", "file002"), 100000, 1 * 1024) self.checkSplit(pathJoin(dailyDir, "system3", "file003"), 100001, 1 * 1024) def testSplitDailyDir_007(self): """ Test with 9,999 byte limit, chopped down to 1000 bytes """ self.extractTar("tree21") dailyDir = self.buildPath(["tree21", "2007", "01", "01"]) self.assertTrue(os.path.exists(dailyDir) and os.path.isdir(dailyDir)) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) sizeLimit = ByteQuantity("9999", UNIT_BYTES) splitSize = ByteQuantity("1000", UNIT_BYTES) _splitDailyDir(dailyDir, sizeLimit, splitSize, None, None) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system1", "file001.a.b"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system1", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system1", "file003"))) self.assertTrue(os.path.exists(pathJoin(dailyDir, "system2", "file001"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system2", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system2", "file003"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file001"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file002"))) self.assertFalse(os.path.exists(pathJoin(dailyDir, "system3", "file003"))) self.checkSplit(pathJoin(dailyDir, "system1", "file002"), 32000, 1000) self.checkSplit(pathJoin(dailyDir, "system1", "file003"), 320000, 1000) self.checkSplit(pathJoin(dailyDir, "system2", "file002"), 10000, 1000) self.checkSplit(pathJoin(dailyDir, "system2", "file003"), 100000, 1000) self.checkSplit(pathJoin(dailyDir, "system3", "file001"), 99999, 1000) self.checkSplit(pathJoin(dailyDir, "system3", "file002"), 100000, 1000) self.checkSplit(pathJoin(dailyDir, "system3", "file003"), 100001, 1000) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1649563 cedar_backup3-3.8.1/tests/test_subversion.py0000644000000000000000000032405514567004737016137 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2005-2007,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests Subversion extension functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/extend/subversion.py. Code Coverage ============= This module contains individual tests for the many of the public functions and classes implemented in extend/subversion.py. There are also tests for several of the private methods. Unfortunately, it's rather difficult to test this code in an automated fashion, even if you have access to Subversion, since the actual backup would need to have access to real Subversion repositories. Because of this, there aren't any tests below that actually back up repositories. As a compromise, I test some of the private methods in the implementation. Normally, I don't like to test private methods, but in this case, testing the private methods will help give us some reasonable confidence in the code even if we can't talk to Subversion successfully. This isn't perfect, but it's better than nothing. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Testing XML Extraction ====================== It's difficult to validated that generated XML is exactly "right", especially when dealing with pretty-printed XML. We can't just provide a constant string and say "the result must match this". Instead, what we do is extract a node, build some XML from it, and then feed that XML back into another object's constructor. If that parse process succeeds and the old object is equal to the new object, we assume that the extract was successful. It would arguably be better if we could do a completely independent check - but implementing that check would be equivalent to re-implementing all of the existing functionality that we're validating here! After all, the most important thing is that data can move seamlessly from object to XML document and back to object. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a SUBVERSIONTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from CedarBackup3.extend.subversion import BDBRepository, FSFSRepository, LocalConfig, Repository, RepositoryDir, SubversionConfig from CedarBackup3.testutil import configureLogging, failUnlessAssignRaises, findResources from CedarBackup3.xmlutil import createOutputDom, serializeDom ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = [ "./data", "./tests/data", ] RESOURCES = [ "subversion.conf.1", "subversion.conf.2", "subversion.conf.3", "subversion.conf.4", "subversion.conf.5", "subversion.conf.6", "subversion.conf.7", ] ####################################################################### # Test Case Classes ####################################################################### ########################## # TestBDBRepository class ########################## class TestBDBRepository(unittest.TestCase): """ Tests for the BDBRepository class. *Note:* This class is deprecated. These tests are kept around to make sure that we don't accidentally break the interface. """ ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = BDBRepository() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ repository = BDBRepository() self.assertEqual("BDB", repository.repositoryType) self.assertEqual(None, repository.repositoryPath) self.assertEqual(None, repository.collectMode) self.assertEqual(None, repository.compressMode) def testConstructor_002(self): """ Test constructor with all values filled in. """ repository = BDBRepository("/path/to/it", "daily", "gzip") self.assertEqual("BDB", repository.repositoryType) self.assertEqual("/path/to/it", repository.repositoryPath) self.assertEqual("daily", repository.collectMode) self.assertEqual("gzip", repository.compressMode) # Removed testConstructor_003 after BDBRepository was deprecated def testConstructor_004(self): """ Test assignment of repositoryPath attribute, None value. """ repository = BDBRepository(repositoryPath="/path/to/something") self.assertEqual("/path/to/something", repository.repositoryPath) repository.repositoryPath = None self.assertEqual(None, repository.repositoryPath) def testConstructor_005(self): """ Test assignment of repositoryPath attribute, valid value. """ repository = BDBRepository() self.assertEqual(None, repository.repositoryPath) repository.repositoryPath = "/path/to/whatever" self.assertEqual("/path/to/whatever", repository.repositoryPath) def testConstructor_006(self): """ Test assignment of repositoryPath attribute, invalid value (empty). """ repository = BDBRepository() self.assertEqual(None, repository.repositoryPath) self.failUnlessAssignRaises(ValueError, repository, "repositoryPath", "") self.assertEqual(None, repository.repositoryPath) def testConstructor_007(self): """ Test assignment of repositoryPath attribute, invalid value (not absolute). """ repository = BDBRepository() self.assertEqual(None, repository.repositoryPath) self.failUnlessAssignRaises(ValueError, repository, "repositoryPath", "relative/path") self.assertEqual(None, repository.repositoryPath) def testConstructor_008(self): """ Test assignment of collectMode attribute, None value. """ repository = BDBRepository(collectMode="daily") self.assertEqual("daily", repository.collectMode) repository.collectMode = None self.assertEqual(None, repository.collectMode) def testConstructor_009(self): """ Test assignment of collectMode attribute, valid value. """ repository = BDBRepository() self.assertEqual(None, repository.collectMode) repository.collectMode = "daily" self.assertEqual("daily", repository.collectMode) repository.collectMode = "weekly" self.assertEqual("weekly", repository.collectMode) repository.collectMode = "incr" self.assertEqual("incr", repository.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, invalid value (empty). """ repository = BDBRepository() self.assertEqual(None, repository.collectMode) self.failUnlessAssignRaises(ValueError, repository, "collectMode", "") self.assertEqual(None, repository.collectMode) def testConstructor_011(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ repository = BDBRepository() self.assertEqual(None, repository.collectMode) self.failUnlessAssignRaises(ValueError, repository, "collectMode", "monthly") self.assertEqual(None, repository.collectMode) def testConstructor_012(self): """ Test assignment of compressMode attribute, None value. """ repository = BDBRepository(compressMode="gzip") self.assertEqual("gzip", repository.compressMode) repository.compressMode = None self.assertEqual(None, repository.compressMode) def testConstructor_013(self): """ Test assignment of compressMode attribute, valid value. """ repository = BDBRepository() self.assertEqual(None, repository.compressMode) repository.compressMode = "none" self.assertEqual("none", repository.compressMode) repository.compressMode = "bzip2" self.assertEqual("bzip2", repository.compressMode) repository.compressMode = "gzip" self.assertEqual("gzip", repository.compressMode) def testConstructor_014(self): """ Test assignment of compressMode attribute, invalid value (empty). """ repository = BDBRepository() self.assertEqual(None, repository.compressMode) self.failUnlessAssignRaises(ValueError, repository, "compressMode", "") self.assertEqual(None, repository.compressMode) def testConstructor_015(self): """ Test assignment of compressMode attribute, invalid value (not in list). """ repository = BDBRepository() self.assertEqual(None, repository.compressMode) self.failUnlessAssignRaises(ValueError, repository, "compressMode", "compress") self.assertEqual(None, repository.compressMode) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ repository1 = BDBRepository() repository2 = BDBRepository() self.assertEqual(repository1, repository2) self.assertTrue(repository1 == repository2) self.assertTrue(not repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(repository1 >= repository2) self.assertTrue(not repository1 != repository2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ repository1 = BDBRepository("/path", "daily", "gzip") repository2 = BDBRepository("/path", "daily", "gzip") self.assertEqual(repository1, repository2) self.assertTrue(repository1 == repository2) self.assertTrue(not repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(repository1 >= repository2) self.assertTrue(not repository1 != repository2) def testComparison_003(self): """ Test comparison of two differing objects, repositoryPath differs (one None). """ repository1 = BDBRepository() repository2 = BDBRepository(repositoryPath="/zippy") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_004(self): """ Test comparison of two differing objects, repositoryPath differs. """ repository1 = BDBRepository("/path", "daily", "gzip") repository2 = BDBRepository("/zippy", "daily", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_005(self): """ Test comparison of two differing objects, collectMode differs (one None). """ repository1 = BDBRepository() repository2 = BDBRepository(collectMode="incr") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs. """ repository1 = BDBRepository("/path", "daily", "gzip") repository2 = BDBRepository("/path", "incr", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_007(self): """ Test comparison of two differing objects, compressMode differs (one None). """ repository1 = BDBRepository() repository2 = BDBRepository(compressMode="gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs. """ repository1 = BDBRepository("/path", "daily", "bzip2") repository2 = BDBRepository("/path", "daily", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) ########################### # TestFSFSRepository class ########################### class TestFSFSRepository(unittest.TestCase): """ Tests for the FSFSRepository class. *Note:* This class is deprecated. These tests are kept around to make sure that we don't accidentally break the interface. """ ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = FSFSRepository() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ repository = FSFSRepository() self.assertEqual("FSFS", repository.repositoryType) self.assertEqual(None, repository.repositoryPath) self.assertEqual(None, repository.collectMode) self.assertEqual(None, repository.compressMode) def testConstructor_002(self): """ Test constructor with all values filled in. """ repository = FSFSRepository("/path/to/it", "daily", "gzip") self.assertEqual("FSFS", repository.repositoryType) self.assertEqual("/path/to/it", repository.repositoryPath) self.assertEqual("daily", repository.collectMode) self.assertEqual("gzip", repository.compressMode) # Removed testConstructor_003 after FSFSRepository was deprecated def testConstructor_004(self): """ Test assignment of repositoryPath attribute, None value. """ repository = FSFSRepository(repositoryPath="/path/to/something") self.assertEqual("/path/to/something", repository.repositoryPath) repository.repositoryPath = None self.assertEqual(None, repository.repositoryPath) def testConstructor_005(self): """ Test assignment of repositoryPath attribute, valid value. """ repository = FSFSRepository() self.assertEqual(None, repository.repositoryPath) repository.repositoryPath = "/path/to/whatever" self.assertEqual("/path/to/whatever", repository.repositoryPath) def testConstructor_006(self): """ Test assignment of repositoryPath attribute, invalid value (empty). """ repository = FSFSRepository() self.assertEqual(None, repository.repositoryPath) self.failUnlessAssignRaises(ValueError, repository, "repositoryPath", "") self.assertEqual(None, repository.repositoryPath) def testConstructor_007(self): """ Test assignment of repositoryPath attribute, invalid value (not absolute). """ repository = FSFSRepository() self.assertEqual(None, repository.repositoryPath) self.failUnlessAssignRaises(ValueError, repository, "repositoryPath", "relative/path") self.assertEqual(None, repository.repositoryPath) def testConstructor_008(self): """ Test assignment of collectMode attribute, None value. """ repository = FSFSRepository(collectMode="daily") self.assertEqual("daily", repository.collectMode) repository.collectMode = None self.assertEqual(None, repository.collectMode) def testConstructor_009(self): """ Test assignment of collectMode attribute, valid value. """ repository = FSFSRepository() self.assertEqual(None, repository.collectMode) repository.collectMode = "daily" self.assertEqual("daily", repository.collectMode) repository.collectMode = "weekly" self.assertEqual("weekly", repository.collectMode) repository.collectMode = "incr" self.assertEqual("incr", repository.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, invalid value (empty). """ repository = FSFSRepository() self.assertEqual(None, repository.collectMode) self.failUnlessAssignRaises(ValueError, repository, "collectMode", "") self.assertEqual(None, repository.collectMode) def testConstructor_011(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ repository = FSFSRepository() self.assertEqual(None, repository.collectMode) self.failUnlessAssignRaises(ValueError, repository, "collectMode", "monthly") self.assertEqual(None, repository.collectMode) def testConstructor_012(self): """ Test assignment of compressMode attribute, None value. """ repository = FSFSRepository(compressMode="gzip") self.assertEqual("gzip", repository.compressMode) repository.compressMode = None self.assertEqual(None, repository.compressMode) def testConstructor_013(self): """ Test assignment of compressMode attribute, valid value. """ repository = FSFSRepository() self.assertEqual(None, repository.compressMode) repository.compressMode = "none" self.assertEqual("none", repository.compressMode) repository.compressMode = "bzip2" self.assertEqual("bzip2", repository.compressMode) repository.compressMode = "gzip" self.assertEqual("gzip", repository.compressMode) def testConstructor_014(self): """ Test assignment of compressMode attribute, invalid value (empty). """ repository = FSFSRepository() self.assertEqual(None, repository.compressMode) self.failUnlessAssignRaises(ValueError, repository, "compressMode", "") self.assertEqual(None, repository.compressMode) def testConstructor_015(self): """ Test assignment of compressMode attribute, invalid value (not in list). """ repository = FSFSRepository() self.assertEqual(None, repository.compressMode) self.failUnlessAssignRaises(ValueError, repository, "compressMode", "compress") self.assertEqual(None, repository.compressMode) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ repository1 = FSFSRepository() repository2 = FSFSRepository() self.assertEqual(repository1, repository2) self.assertTrue(repository1 == repository2) self.assertTrue(not repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(repository1 >= repository2) self.assertTrue(not repository1 != repository2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ repository1 = FSFSRepository("/path", "daily", "gzip") repository2 = FSFSRepository("/path", "daily", "gzip") self.assertEqual(repository1, repository2) self.assertTrue(repository1 == repository2) self.assertTrue(not repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(repository1 >= repository2) self.assertTrue(not repository1 != repository2) def testComparison_003(self): """ Test comparison of two differing objects, repositoryPath differs (one None). """ repository1 = FSFSRepository() repository2 = FSFSRepository(repositoryPath="/zippy") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_004(self): """ Test comparison of two differing objects, repositoryPath differs. """ repository1 = FSFSRepository("/path", "daily", "gzip") repository2 = FSFSRepository("/zippy", "daily", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_005(self): """ Test comparison of two differing objects, collectMode differs (one None). """ repository1 = FSFSRepository() repository2 = FSFSRepository(collectMode="incr") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs. """ repository1 = FSFSRepository("/path", "daily", "gzip") repository2 = FSFSRepository("/path", "incr", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_007(self): """ Test comparison of two differing objects, compressMode differs (one None). """ repository1 = FSFSRepository() repository2 = FSFSRepository(compressMode="gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs. """ repository1 = FSFSRepository("/path", "daily", "bzip2") repository2 = FSFSRepository("/path", "daily", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) ####################### # TestRepository class ####################### class TestRepository(unittest.TestCase): """Tests for the Repository class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = Repository() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ repository = Repository() self.assertEqual(None, repository.repositoryType) self.assertEqual(None, repository.repositoryPath) self.assertEqual(None, repository.collectMode) self.assertEqual(None, repository.compressMode) def testConstructor_002(self): """ Test constructor with all values filled in. """ repository = Repository("type", "/path/to/it", "daily", "gzip") self.assertEqual("type", repository.repositoryType) self.assertEqual("/path/to/it", repository.repositoryPath) self.assertEqual("daily", repository.collectMode) self.assertEqual("gzip", repository.compressMode) def testConstructor_003(self): """ Test assignment of repositoryType attribute, None value. """ repository = Repository(repositoryType="type") self.assertEqual("type", repository.repositoryType) repository.repositoryType = None self.assertEqual(None, repository.repositoryType) def testConstructor_004(self): """ Test assignment of repositoryType attribute, non-None value. """ repository = Repository() self.assertEqual(None, repository.repositoryType) repository.repositoryType = "" self.assertEqual("", repository.repositoryType) repository.repositoryType = "test" self.assertEqual("test", repository.repositoryType) def testConstructor_005(self): """ Test assignment of repositoryPath attribute, None value. """ repository = Repository(repositoryPath="/path/to/something") self.assertEqual("/path/to/something", repository.repositoryPath) repository.repositoryPath = None self.assertEqual(None, repository.repositoryPath) def testConstructor_006(self): """ Test assignment of repositoryPath attribute, valid value. """ repository = Repository() self.assertEqual(None, repository.repositoryPath) repository.repositoryPath = "/path/to/whatever" self.assertEqual("/path/to/whatever", repository.repositoryPath) def testConstructor_007(self): """ Test assignment of repositoryPath attribute, invalid value (empty). """ repository = Repository() self.assertEqual(None, repository.repositoryPath) self.failUnlessAssignRaises(ValueError, repository, "repositoryPath", "") self.assertEqual(None, repository.repositoryPath) def testConstructor_008(self): """ Test assignment of repositoryPath attribute, invalid value (not absolute). """ repository = Repository() self.assertEqual(None, repository.repositoryPath) self.failUnlessAssignRaises(ValueError, repository, "repositoryPath", "relative/path") self.assertEqual(None, repository.repositoryPath) def testConstructor_009(self): """ Test assignment of collectMode attribute, None value. """ repository = Repository(collectMode="daily") self.assertEqual("daily", repository.collectMode) repository.collectMode = None self.assertEqual(None, repository.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, valid value. """ repository = Repository() self.assertEqual(None, repository.collectMode) repository.collectMode = "daily" self.assertEqual("daily", repository.collectMode) repository.collectMode = "weekly" self.assertEqual("weekly", repository.collectMode) repository.collectMode = "incr" self.assertEqual("incr", repository.collectMode) def testConstructor_011(self): """ Test assignment of collectMode attribute, invalid value (empty). """ repository = Repository() self.assertEqual(None, repository.collectMode) self.failUnlessAssignRaises(ValueError, repository, "collectMode", "") self.assertEqual(None, repository.collectMode) def testConstructor_012(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ repository = Repository() self.assertEqual(None, repository.collectMode) self.failUnlessAssignRaises(ValueError, repository, "collectMode", "monthly") self.assertEqual(None, repository.collectMode) def testConstructor_013(self): """ Test assignment of compressMode attribute, None value. """ repository = Repository(compressMode="gzip") self.assertEqual("gzip", repository.compressMode) repository.compressMode = None self.assertEqual(None, repository.compressMode) def testConstructor_014(self): """ Test assignment of compressMode attribute, valid value. """ repository = Repository() self.assertEqual(None, repository.compressMode) repository.compressMode = "none" self.assertEqual("none", repository.compressMode) repository.compressMode = "bzip2" self.assertEqual("bzip2", repository.compressMode) repository.compressMode = "gzip" self.assertEqual("gzip", repository.compressMode) def testConstructor_015(self): """ Test assignment of compressMode attribute, invalid value (empty). """ repository = Repository() self.assertEqual(None, repository.compressMode) self.failUnlessAssignRaises(ValueError, repository, "compressMode", "") self.assertEqual(None, repository.compressMode) def testConstructor_016(self): """ Test assignment of compressMode attribute, invalid value (not in list). """ repository = Repository() self.assertEqual(None, repository.compressMode) self.failUnlessAssignRaises(ValueError, repository, "compressMode", "compress") self.assertEqual(None, repository.compressMode) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ repository1 = Repository() repository2 = Repository() self.assertEqual(repository1, repository2) self.assertTrue(repository1 == repository2) self.assertTrue(not repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(repository1 >= repository2) self.assertTrue(not repository1 != repository2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ repository1 = Repository("type", "/path", "daily", "gzip") repository2 = Repository("type", "/path", "daily", "gzip") self.assertEqual(repository1, repository2) self.assertTrue(repository1 == repository2) self.assertTrue(not repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(repository1 >= repository2) self.assertTrue(not repository1 != repository2) def testComparison_003(self): """ Test comparison of two differing objects, repositoryType differs (one None). """ repository1 = Repository() repository2 = Repository(repositoryType="type") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_004(self): """ Test comparison of two differing objects, repositoryType differs. """ repository1 = Repository("other", "/path", "daily", "gzip") repository2 = Repository("type", "/path", "daily", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_004a(self): """ Test comparison of two differing objects, repositoryPath differs (one None). """ repository1 = Repository() repository2 = Repository(repositoryPath="/zippy") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_005(self): """ Test comparison of two differing objects, repositoryPath differs. """ repository1 = Repository("type", "/path", "daily", "gzip") repository2 = Repository("type", "/zippy", "daily", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs (one None). """ repository1 = Repository() repository2 = Repository(collectMode="incr") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_007(self): """ Test comparison of two differing objects, collectMode differs. """ repository1 = Repository("type", "/path", "daily", "gzip") repository2 = Repository("type", "/path", "incr", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs (one None). """ repository1 = Repository() repository2 = Repository(compressMode="gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) def testComparison_009(self): """ Test comparison of two differing objects, compressMode differs. """ repository1 = Repository("type", "/path", "daily", "bzip2") repository2 = Repository("type", "/path", "daily", "gzip") self.assertNotEqual(repository1, repository2) self.assertTrue(not repository1 == repository2) self.assertTrue(repository1 < repository2) self.assertTrue(repository1 <= repository2) self.assertTrue(not repository1 > repository2) self.assertTrue(not repository1 >= repository2) self.assertTrue(repository1 != repository2) ########################## # TestRepositoryDir class ########################## class TestRepositoryDir(unittest.TestCase): """Tests for the RepositoryDir class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = RepositoryDir() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.repositoryType) self.assertEqual(None, repositoryDir.directoryPath) self.assertEqual(None, repositoryDir.collectMode) self.assertEqual(None, repositoryDir.compressMode) self.assertEqual(None, repositoryDir.relativeExcludePaths) self.assertEqual(None, repositoryDir.excludePatterns) def testConstructor_002(self): """ Test constructor with all values filled in. """ repositoryDir = RepositoryDir("type", "/path/to/it", "daily", "gzip", ["whatever"], [".*software.*"]) self.assertEqual("type", repositoryDir.repositoryType) self.assertEqual("/path/to/it", repositoryDir.directoryPath) self.assertEqual("daily", repositoryDir.collectMode) self.assertEqual("gzip", repositoryDir.compressMode) self.assertEqual(["whatever"], repositoryDir.relativeExcludePaths) self.assertEqual([".*software.*"], repositoryDir.excludePatterns) def testConstructor_003(self): """ Test assignment of repositoryType attribute, None value. """ repositoryDir = RepositoryDir(repositoryType="type") self.assertEqual("type", repositoryDir.repositoryType) repositoryDir.repositoryType = None self.assertEqual(None, repositoryDir.repositoryType) def testConstructor_004(self): """ Test assignment of repositoryType attribute, non-None value. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.repositoryType) repositoryDir.repositoryType = "" self.assertEqual("", repositoryDir.repositoryType) repositoryDir.repositoryType = "test" self.assertEqual("test", repositoryDir.repositoryType) def testConstructor_005(self): """ Test assignment of directoryPath attribute, None value. """ repositoryDir = RepositoryDir(directoryPath="/path/to/something") self.assertEqual("/path/to/something", repositoryDir.directoryPath) repositoryDir.directoryPath = None self.assertEqual(None, repositoryDir.directoryPath) def testConstructor_006(self): """ Test assignment of directoryPath attribute, valid value. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.directoryPath) repositoryDir.directoryPath = "/path/to/whatever" self.assertEqual("/path/to/whatever", repositoryDir.directoryPath) def testConstructor_007(self): """ Test assignment of directoryPath attribute, invalid value (empty). """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.directoryPath) self.failUnlessAssignRaises(ValueError, repositoryDir, "directoryPath", "") self.assertEqual(None, repositoryDir.directoryPath) def testConstructor_008(self): """ Test assignment of directoryPath attribute, invalid value (not absolute). """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.directoryPath) self.failUnlessAssignRaises(ValueError, repositoryDir, "directoryPath", "relative/path") self.assertEqual(None, repositoryDir.directoryPath) def testConstructor_009(self): """ Test assignment of collectMode attribute, None value. """ repositoryDir = RepositoryDir(collectMode="daily") self.assertEqual("daily", repositoryDir.collectMode) repositoryDir.collectMode = None self.assertEqual(None, repositoryDir.collectMode) def testConstructor_010(self): """ Test assignment of collectMode attribute, valid value. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.collectMode) repositoryDir.collectMode = "daily" self.assertEqual("daily", repositoryDir.collectMode) repositoryDir.collectMode = "weekly" self.assertEqual("weekly", repositoryDir.collectMode) repositoryDir.collectMode = "incr" self.assertEqual("incr", repositoryDir.collectMode) def testConstructor_011(self): """ Test assignment of collectMode attribute, invalid value (empty). """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.collectMode) self.failUnlessAssignRaises(ValueError, repositoryDir, "collectMode", "") self.assertEqual(None, repositoryDir.collectMode) def testConstructor_012(self): """ Test assignment of collectMode attribute, invalid value (not in list). """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.collectMode) self.failUnlessAssignRaises(ValueError, repositoryDir, "collectMode", "monthly") self.assertEqual(None, repositoryDir.collectMode) def testConstructor_013(self): """ Test assignment of compressMode attribute, None value. """ repositoryDir = RepositoryDir(compressMode="gzip") self.assertEqual("gzip", repositoryDir.compressMode) repositoryDir.compressMode = None self.assertEqual(None, repositoryDir.compressMode) def testConstructor_014(self): """ Test assignment of compressMode attribute, valid value. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.compressMode) repositoryDir.compressMode = "none" self.assertEqual("none", repositoryDir.compressMode) repositoryDir.compressMode = "bzip2" self.assertEqual("bzip2", repositoryDir.compressMode) repositoryDir.compressMode = "gzip" self.assertEqual("gzip", repositoryDir.compressMode) def testConstructor_015(self): """ Test assignment of compressMode attribute, invalid value (empty). """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.compressMode) self.failUnlessAssignRaises(ValueError, repositoryDir, "compressMode", "") self.assertEqual(None, repositoryDir.compressMode) def testConstructor_016(self): """ Test assignment of compressMode attribute, invalid value (not in list). """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.compressMode) self.failUnlessAssignRaises(ValueError, repositoryDir, "compressMode", "compress") self.assertEqual(None, repositoryDir.compressMode) def testConstructor_017(self): """ Test assignment of relativeExcludePaths attribute, None value. """ repositoryDir = RepositoryDir(relativeExcludePaths=[]) self.assertEqual([], repositoryDir.relativeExcludePaths) repositoryDir.relativeExcludePaths = None self.assertEqual(None, repositoryDir.relativeExcludePaths) def testConstructor_018(self): """ Test assignment of relativeExcludePaths attribute, [] value. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.relativeExcludePaths) repositoryDir.relativeExcludePaths = [] self.assertEqual([], repositoryDir.relativeExcludePaths) def testConstructor_019(self): """ Test assignment of relativeExcludePaths attribute, single valid entry. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.relativeExcludePaths) repositoryDir.relativeExcludePaths = [ "stuff", ] self.assertEqual(["stuff"], repositoryDir.relativeExcludePaths) repositoryDir.relativeExcludePaths.insert(0, "bogus") self.assertEqual(["bogus", "stuff"], repositoryDir.relativeExcludePaths) def testConstructor_020(self): """ Test assignment of relativeExcludePaths attribute, multiple valid entries. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.relativeExcludePaths) repositoryDir.relativeExcludePaths = [ "bogus", "stuff", ] self.assertEqual(["bogus", "stuff"], repositoryDir.relativeExcludePaths) repositoryDir.relativeExcludePaths.append("more") self.assertEqual(["bogus", "stuff", "more"], repositoryDir.relativeExcludePaths) def testConstructor_021(self): """ Test assignment of excludePatterns attribute, None value. """ repositoryDir = RepositoryDir(excludePatterns=[]) self.assertEqual([], repositoryDir.excludePatterns) repositoryDir.excludePatterns = None self.assertEqual(None, repositoryDir.excludePatterns) def testConstructor_022(self): """ Test assignment of excludePatterns attribute, [] value. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.excludePatterns) repositoryDir.excludePatterns = [] self.assertEqual([], repositoryDir.excludePatterns) def testConstructor_023(self): """ Test assignment of excludePatterns attribute, single valid entry. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.excludePatterns) repositoryDir.excludePatterns = [ "valid", ] self.assertEqual(["valid"], repositoryDir.excludePatterns) repositoryDir.excludePatterns.append("more") self.assertEqual(["valid", "more"], repositoryDir.excludePatterns) def testConstructor_024(self): """ Test assignment of excludePatterns attribute, multiple valid entries. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.excludePatterns) repositoryDir.excludePatterns = [ "valid", "more", ] self.assertEqual(["valid", "more"], repositoryDir.excludePatterns) repositoryDir.excludePatterns.insert(1, "bogus") self.assertEqual(["valid", "bogus", "more"], repositoryDir.excludePatterns) def testConstructor_025(self): """ Test assignment of excludePatterns attribute, single invalid entry. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.excludePatterns) self.failUnlessAssignRaises(ValueError, repositoryDir, "excludePatterns", ["*.jpg"]) self.assertEqual(None, repositoryDir.excludePatterns) def testConstructor_026(self): """ Test assignment of excludePatterns attribute, multiple invalid entries. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.excludePatterns) self.failUnlessAssignRaises(ValueError, repositoryDir, "excludePatterns", ["*.jpg", "*"]) self.assertEqual(None, repositoryDir.excludePatterns) def testConstructor_027(self): """ Test assignment of excludePatterns attribute, mixed valid and invalid entries. """ repositoryDir = RepositoryDir() self.assertEqual(None, repositoryDir.excludePatterns) self.failUnlessAssignRaises(ValueError, repositoryDir, "excludePatterns", ["*.jpg", "valid"]) self.assertEqual(None, repositoryDir.excludePatterns) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ repositoryDir1 = RepositoryDir() repositoryDir2 = RepositoryDir() self.assertEqual(repositoryDir1, repositoryDir2) self.assertTrue(repositoryDir1 == repositoryDir2) self.assertTrue(not repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(repositoryDir1 >= repositoryDir2) self.assertTrue(not repositoryDir1 != repositoryDir2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ repositoryDir1 = RepositoryDir("type", "/path", "daily", "gzip") repositoryDir2 = RepositoryDir("type", "/path", "daily", "gzip") self.assertEqual(repositoryDir1, repositoryDir2) self.assertTrue(repositoryDir1 == repositoryDir2) self.assertTrue(not repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(repositoryDir1 >= repositoryDir2) self.assertTrue(not repositoryDir1 != repositoryDir2) def testComparison_003(self): """ Test comparison of two differing objects, repositoryType differs (one None). """ repositoryDir1 = RepositoryDir() repositoryDir2 = RepositoryDir(repositoryType="type") self.assertNotEqual(repositoryDir1, repositoryDir2) self.assertTrue(not repositoryDir1 == repositoryDir2) self.assertTrue(repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(not repositoryDir1 >= repositoryDir2) self.assertTrue(repositoryDir1 != repositoryDir2) def testComparison_004(self): """ Test comparison of two differing objects, repositoryType differs. """ repositoryDir1 = RepositoryDir("other", "/path", "daily", "gzip") repositoryDir2 = RepositoryDir("type", "/path", "daily", "gzip") self.assertNotEqual(repositoryDir1, repositoryDir2) self.assertTrue(not repositoryDir1 == repositoryDir2) self.assertTrue(repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(not repositoryDir1 >= repositoryDir2) self.assertTrue(repositoryDir1 != repositoryDir2) def testComparison_004a(self): """ Test comparison of two differing objects, directoryPath differs (one None). """ repositoryDir1 = RepositoryDir() repositoryDir2 = RepositoryDir(directoryPath="/zippy") self.assertNotEqual(repositoryDir1, repositoryDir2) self.assertTrue(not repositoryDir1 == repositoryDir2) self.assertTrue(repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(not repositoryDir1 >= repositoryDir2) self.assertTrue(repositoryDir1 != repositoryDir2) def testComparison_005(self): """ Test comparison of two differing objects, directoryPath differs. """ repositoryDir1 = RepositoryDir("type", "/path", "daily", "gzip") repositoryDir2 = RepositoryDir("type", "/zippy", "daily", "gzip") self.assertNotEqual(repositoryDir1, repositoryDir2) self.assertTrue(not repositoryDir1 == repositoryDir2) self.assertTrue(repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(not repositoryDir1 >= repositoryDir2) self.assertTrue(repositoryDir1 != repositoryDir2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs (one None). """ repositoryDir1 = RepositoryDir() repositoryDir2 = RepositoryDir(collectMode="incr") self.assertNotEqual(repositoryDir1, repositoryDir2) self.assertTrue(not repositoryDir1 == repositoryDir2) self.assertTrue(repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(not repositoryDir1 >= repositoryDir2) self.assertTrue(repositoryDir1 != repositoryDir2) def testComparison_007(self): """ Test comparison of two differing objects, collectMode differs. """ repositoryDir1 = RepositoryDir("type", "/path", "daily", "gzip") repositoryDir2 = RepositoryDir("type", "/path", "incr", "gzip") self.assertNotEqual(repositoryDir1, repositoryDir2) self.assertTrue(not repositoryDir1 == repositoryDir2) self.assertTrue(repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(not repositoryDir1 >= repositoryDir2) self.assertTrue(repositoryDir1 != repositoryDir2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs (one None). """ repositoryDir1 = RepositoryDir() repositoryDir2 = RepositoryDir(compressMode="gzip") self.assertNotEqual(repositoryDir1, repositoryDir2) self.assertTrue(not repositoryDir1 == repositoryDir2) self.assertTrue(repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(not repositoryDir1 >= repositoryDir2) self.assertTrue(repositoryDir1 != repositoryDir2) def testComparison_009(self): """ Test comparison of two differing objects, compressMode differs. """ repositoryDir1 = RepositoryDir("type", "/path", "daily", "bzip2") repositoryDir2 = RepositoryDir("type", "/path", "daily", "gzip") self.assertNotEqual(repositoryDir1, repositoryDir2) self.assertTrue(not repositoryDir1 == repositoryDir2) self.assertTrue(repositoryDir1 < repositoryDir2) self.assertTrue(repositoryDir1 <= repositoryDir2) self.assertTrue(not repositoryDir1 > repositoryDir2) self.assertTrue(not repositoryDir1 >= repositoryDir2) self.assertTrue(repositoryDir1 != repositoryDir2) ############################# # TestSubversionConfig class ############################# class TestSubversionConfig(unittest.TestCase): """Tests for the SubversionConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = SubversionConfig() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no values filled in. """ subversion = SubversionConfig() self.assertEqual(None, subversion.collectMode) self.assertEqual(None, subversion.compressMode) self.assertEqual(None, subversion.repositories) def testConstructor_002(self): """ Test constructor with all values filled in, with valid values, repositories=None. """ subversion = SubversionConfig("daily", "gzip", None) self.assertEqual("daily", subversion.collectMode) self.assertEqual("gzip", subversion.compressMode) self.assertEqual(None, subversion.repositories) def testConstructor_003(self): """ Test constructor with all values filled in, with valid values, no repositories. """ subversion = SubversionConfig("daily", "gzip", []) self.assertEqual("daily", subversion.collectMode) self.assertEqual("gzip", subversion.compressMode) self.assertEqual([], subversion.repositories) def testConstructor_004(self): """ Test constructor with all values filled in, with valid values, with one repository. """ repositories = [ Repository(), ] subversion = SubversionConfig("daily", "gzip", repositories) self.assertEqual("daily", subversion.collectMode) self.assertEqual("gzip", subversion.compressMode) self.assertEqual(repositories, subversion.repositories) def testConstructor_005(self): """ Test constructor with all values filled in, with valid values, with multiple repositories. """ repositories = [ Repository(collectMode="daily"), Repository(collectMode="weekly"), ] subversion = SubversionConfig("daily", "gzip", repositories=repositories) self.assertEqual("daily", subversion.collectMode) self.assertEqual("gzip", subversion.compressMode) self.assertEqual(repositories, subversion.repositories) def testConstructor_006(self): """ Test assignment of collectMode attribute, None value. """ subversion = SubversionConfig(collectMode="daily") self.assertEqual("daily", subversion.collectMode) subversion.collectMode = None self.assertEqual(None, subversion.collectMode) def testConstructor_007(self): """ Test assignment of collectMode attribute, valid value. """ subversion = SubversionConfig() self.assertEqual(None, subversion.collectMode) subversion.collectMode = "weekly" self.assertEqual("weekly", subversion.collectMode) def testConstructor_008(self): """ Test assignment of collectMode attribute, invalid value (empty). """ subversion = SubversionConfig() self.assertEqual(None, subversion.collectMode) self.failUnlessAssignRaises(ValueError, subversion, "collectMode", "") self.assertEqual(None, subversion.collectMode) def testConstructor_009(self): """ Test assignment of compressMode attribute, None value. """ subversion = SubversionConfig(compressMode="gzip") self.assertEqual("gzip", subversion.compressMode) subversion.compressMode = None self.assertEqual(None, subversion.compressMode) def testConstructor_010(self): """ Test assignment of compressMode attribute, valid value. """ subversion = SubversionConfig() self.assertEqual(None, subversion.compressMode) subversion.compressMode = "bzip2" self.assertEqual("bzip2", subversion.compressMode) def testConstructor_011(self): """ Test assignment of compressMode attribute, invalid value (empty). """ subversion = SubversionConfig() self.assertEqual(None, subversion.compressMode) self.failUnlessAssignRaises(ValueError, subversion, "compressMode", "") self.assertEqual(None, subversion.compressMode) def testConstructor_012(self): """ Test assignment of repositories attribute, None value. """ subversion = SubversionConfig(repositories=[]) self.assertEqual([], subversion.repositories) subversion.repositories = None self.assertEqual(None, subversion.repositories) def testConstructor_013(self): """ Test assignment of repositories attribute, [] value. """ subversion = SubversionConfig() self.assertEqual(None, subversion.repositories) subversion.repositories = [] self.assertEqual([], subversion.repositories) def testConstructor_014(self): """ Test assignment of repositories attribute, single valid entry. """ subversion = SubversionConfig() self.assertEqual(None, subversion.repositories) subversion.repositories = [ Repository(), ] self.assertEqual([Repository()], subversion.repositories) subversion.repositories.append(Repository(collectMode="daily")) self.assertEqual([Repository(), Repository(collectMode="daily")], subversion.repositories) def testConstructor_015(self): """ Test assignment of repositories attribute, multiple valid entries. """ subversion = SubversionConfig() self.assertEqual(None, subversion.repositories) subversion.repositories = [ Repository(collectMode="daily"), Repository(collectMode="weekly"), ] self.assertEqual([Repository(collectMode="daily"), Repository(collectMode="weekly")], subversion.repositories) subversion.repositories.append(Repository(collectMode="incr")) self.assertEqual( [Repository(collectMode="daily"), Repository(collectMode="weekly"), Repository(collectMode="incr")], subversion.repositories, ) def testConstructor_016(self): """ Test assignment of repositories attribute, single invalid entry (None). """ subversion = SubversionConfig() self.assertEqual(None, subversion.repositories) self.failUnlessAssignRaises(ValueError, subversion, "repositories", [None]) self.assertEqual(None, subversion.repositories) def testConstructor_017(self): """ Test assignment of repositories attribute, single invalid entry (wrong type). """ subversion = SubversionConfig() self.assertEqual(None, subversion.repositories) self.failUnlessAssignRaises(ValueError, subversion, "repositories", [SubversionConfig()]) self.assertEqual(None, subversion.repositories) def testConstructor_018(self): """ Test assignment of repositories attribute, mixed valid and invalid entries. """ subversion = SubversionConfig() self.assertEqual(None, subversion.repositories) self.failUnlessAssignRaises(ValueError, subversion, "repositories", [Repository(), SubversionConfig()]) self.assertEqual(None, subversion.repositories) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ subversion1 = SubversionConfig() subversion2 = SubversionConfig() self.assertEqual(subversion1, subversion2) self.assertTrue(subversion1 == subversion2) self.assertTrue(not subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(subversion1 >= subversion2) self.assertTrue(not subversion1 != subversion2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None, list None. """ subversion1 = SubversionConfig("daily", "gzip", None) subversion2 = SubversionConfig("daily", "gzip", None) self.assertEqual(subversion1, subversion2) self.assertTrue(subversion1 == subversion2) self.assertTrue(not subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(subversion1 >= subversion2) self.assertTrue(not subversion1 != subversion2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes non-None, list empty. """ subversion1 = SubversionConfig("daily", "gzip", []) subversion2 = SubversionConfig("daily", "gzip", []) self.assertEqual(subversion1, subversion2) self.assertTrue(subversion1 == subversion2) self.assertTrue(not subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(subversion1 >= subversion2) self.assertTrue(not subversion1 != subversion2) def testComparison_004(self): """ Test comparison of two identical objects, all attributes non-None, list non-empty. """ subversion1 = SubversionConfig("daily", "gzip", [Repository()]) subversion2 = SubversionConfig("daily", "gzip", [Repository()]) self.assertEqual(subversion1, subversion2) self.assertTrue(subversion1 == subversion2) self.assertTrue(not subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(subversion1 >= subversion2) self.assertTrue(not subversion1 != subversion2) def testComparison_005(self): """ Test comparison of two differing objects, collectMode differs (one None). """ subversion1 = SubversionConfig() subversion2 = SubversionConfig(collectMode="daily") self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) def testComparison_006(self): """ Test comparison of two differing objects, collectMode differs. """ subversion1 = SubversionConfig("daily", "gzip", [Repository()]) subversion2 = SubversionConfig("weekly", "gzip", [Repository()]) self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) def testComparison_007(self): """ Test comparison of two differing objects, compressMode differs (one None). """ subversion1 = SubversionConfig() subversion2 = SubversionConfig(compressMode="bzip2") self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) def testComparison_008(self): """ Test comparison of two differing objects, compressMode differs. """ subversion1 = SubversionConfig("daily", "bzip2", [Repository()]) subversion2 = SubversionConfig("daily", "gzip", [Repository()]) self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) def testComparison_009(self): """ Test comparison of two differing objects, repositories differs (one None, one empty). """ subversion1 = SubversionConfig() subversion2 = SubversionConfig(repositories=[]) self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) def testComparison_010(self): """ Test comparison of two differing objects, repositories differs (one None, one not empty). """ subversion1 = SubversionConfig() subversion2 = SubversionConfig(repositories=[Repository()]) self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) def testComparison_011(self): """ Test comparison of two differing objects, repositories differs (one empty, one not empty). """ subversion1 = SubversionConfig("daily", "gzip", []) subversion2 = SubversionConfig("daily", "gzip", [Repository()]) self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) def testComparison_012(self): """ Test comparison of two differing objects, repositories differs (both not empty). """ subversion1 = SubversionConfig("daily", "gzip", [Repository()]) subversion2 = SubversionConfig("daily", "gzip", [Repository(), Repository()]) self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) def testComparison_013(self): """ Test comparison of two differing objects, repositories differs (both not empty). """ subversion1 = SubversionConfig("daily", "gzip", [Repository(repositoryType="other")]) subversion2 = SubversionConfig("daily", "gzip", [Repository(repositoryType="type")]) self.assertNotEqual(subversion1, subversion2) self.assertTrue(not subversion1 == subversion2) self.assertTrue(subversion1 < subversion2) self.assertTrue(subversion1 <= subversion2) self.assertTrue(not subversion1 > subversion2) self.assertTrue(not subversion1 >= subversion2) self.assertTrue(subversion1 != subversion2) ######################## # TestLocalConfig class ######################## class TestLocalConfig(unittest.TestCase): """Tests for the LocalConfig class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() ################ # Setup methods ################ def setUp(self): try: self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) def validateAddConfig(self, origConfig): """ Validates that document dumped from ``LocalConfig.addConfig`` results in identical object. We dump a document containing just the subversion configuration, and then make sure that if we push that document back into the ``LocalConfig`` object, that the resulting object matches the original. The ``self.failUnlessEqual`` method is used for the validation, so if the method call returns normally, everything is OK. Args: origConfig: Original configuration """ (xmlDom, parentNode) = createOutputDom() origConfig.addConfig(xmlDom, parentNode) xmlData = serializeDom(xmlDom) newConfig = LocalConfig(xmlData=xmlData, validate=False) self.assertEqual(origConfig, newConfig) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = LocalConfig() obj.__repr__() obj.__str__() ##################################################### # Test basic constructor and attribute functionality ##################################################### def testConstructor_001(self): """ Test empty constructor, validate=False. """ config = LocalConfig(validate=False) self.assertEqual(None, config.subversion) def testConstructor_002(self): """ Test empty constructor, validate=True. """ config = LocalConfig(validate=True) self.assertEqual(None, config.subversion) def testConstructor_003(self): """ Test with empty config document as both data and file, validate=False. """ path = self.resources["subversion.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlData=contents, xmlPath=path, validate=False) def testConstructor_004(self): """ Test assignment of subversion attribute, None value. """ config = LocalConfig() config.subversion = None self.assertEqual(None, config.subversion) def testConstructor_005(self): """ Test assignment of subversion attribute, valid value. """ config = LocalConfig() config.subversion = SubversionConfig() self.assertEqual(SubversionConfig(), config.subversion) def testConstructor_006(self): """ Test assignment of subversion attribute, invalid value (not SubversionConfig). """ config = LocalConfig() self.failUnlessAssignRaises(ValueError, config, "subversion", "STRING!") ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes None. """ config1 = LocalConfig() config2 = LocalConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes non-None. """ config1 = LocalConfig() config1.subversion = SubversionConfig() config2 = LocalConfig() config2.subversion = SubversionConfig() self.assertEqual(config1, config2) self.assertTrue(config1 == config2) self.assertTrue(not config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(config1 >= config2) self.assertTrue(not config1 != config2) def testComparison_003(self): """ Test comparison of two differing objects, subversion differs (one None). """ config1 = LocalConfig() config2 = LocalConfig() config2.subversion = SubversionConfig() self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) def testComparison_004(self): """ Test comparison of two differing objects, subversion differs. """ config1 = LocalConfig() config1.subversion = SubversionConfig(collectMode="daily") config2 = LocalConfig() config2.subversion = SubversionConfig(collectMode="weekly") self.assertNotEqual(config1, config2) self.assertTrue(not config1 == config2) self.assertTrue(config1 < config2) self.assertTrue(config1 <= config2) self.assertTrue(not config1 > config2) self.assertTrue(not config1 >= config2) self.assertTrue(config1 != config2) ###################### # Test validate logic ###################### def testValidate_001(self): """ Test validate on a None subversion section. """ config = LocalConfig() config.subversion = None self.assertRaises(ValueError, config.validate) def testValidate_002(self): """ Test validate on an empty subversion section. """ config = LocalConfig() config.subversion = SubversionConfig() self.assertRaises(ValueError, config.validate) def testValidate_003(self): """ Test validate on a non-empty subversion section, repositories=None. """ config = LocalConfig() config.subversion = SubversionConfig("weekly", "gzip", None) self.assertRaises(ValueError, config.validate) def testValidate_004(self): """ Test validate on a non-empty subversion section, repositories=[]. """ config = LocalConfig() config.subversion = SubversionConfig("weekly", "gzip", []) self.assertRaises(ValueError, config.validate) def testValidate_005(self): """ Test validate on a non-empty subversion section, non-empty repositories, defaults set, no values on repositories. """ repositories = [Repository(repositoryPath="/one"), Repository(repositoryPath="/two")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "daily" config.subversion.compressMode = "gzip" config.subversion.repositories = repositories config.validate() def testValidate_006(self): """ Test validate on a non-empty subversion section, non-empty repositories, no defaults set, no values on repositiories. """ repositories = [Repository(repositoryPath="/one"), Repository(repositoryPath="/two")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.repositories = repositories self.assertRaises(ValueError, config.validate) def testValidate_007(self): """ Test validate on a non-empty subversion section, non-empty repositories, no defaults set, both values on repositories. """ repositories = [Repository(repositoryPath="/two", collectMode="weekly", compressMode="gzip")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.repositories = repositories config.validate() def testValidate_008(self): """ Test validate on a non-empty subversion section, non-empty repositories, collectMode only on repositories. """ repositories = [Repository(repositoryPath="/two", collectMode="weekly")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.compressMode = "gzip" config.subversion.repositories = repositories config.validate() def testValidate_009(self): """ Test validate on a non-empty subversion section, non-empty repositories, compressMode only on repositories. """ repositories = [Repository(repositoryPath="/two", compressMode="bzip2")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "weekly" config.subversion.repositories = repositories config.validate() def testValidate_010(self): """ Test validate on a non-empty subversion section, non-empty repositories, compressMode default and on repository. """ repositories = [Repository(repositoryPath="/two", compressMode="bzip2")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "daily" config.subversion.compressMode = "gzip" config.subversion.repositories = repositories config.validate() def testValidate_011(self): """ Test validate on a non-empty subversion section, non-empty repositories, collectMode default and on repository. """ repositories = [Repository(repositoryPath="/two", collectMode="daily")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "daily" config.subversion.compressMode = "gzip" config.subversion.repositories = repositories config.validate() def testValidate_012(self): """ Test validate on a non-empty subversion section, non-empty repositories, collectMode and compressMode default and on repository. """ repositories = [Repository(repositoryPath="/two", collectMode="daily", compressMode="bzip2")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "daily" config.subversion.compressMode = "gzip" config.subversion.repositories = repositories config.validate() def testValidate_013(self): """ Test validate on a non-empty subversion section, repositoryDirs=None. """ config = LocalConfig() config.subversion = SubversionConfig("weekly", "gzip", repositoryDirs=None) self.assertRaises(ValueError, config.validate) def testValidate_014(self): """ Test validate on a non-empty subversion section, repositoryDirs=[]. """ config = LocalConfig() config.subversion = SubversionConfig("weekly", "gzip", repositoryDirs=[]) self.assertRaises(ValueError, config.validate) def testValidate_015(self): """ Test validate on a non-empty subversion section, non-empty repositoryDirs, defaults set, no values on repositoryDirs. """ repositoryDirs = [RepositoryDir(directoryPath="/one"), RepositoryDir(directoryPath="/two")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "daily" config.subversion.compressMode = "gzip" config.subversion.repositoryDirs = repositoryDirs config.validate() def testValidate_016(self): """ Test validate on a non-empty subversion section, non-empty repositoryDirs, no defaults set, no values on repositiories. """ repositoryDirs = [RepositoryDir(directoryPath="/one"), RepositoryDir(directoryPath="/two")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.repositoryDirs = repositoryDirs self.assertRaises(ValueError, config.validate) def testValidate_017(self): """ Test validate on a non-empty subversion section, non-empty repositoryDirs, no defaults set, both values on repositoryDirs. """ repositoryDirs = [RepositoryDir(directoryPath="/two", collectMode="weekly", compressMode="gzip")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.repositoryDirs = repositoryDirs config.validate() def testValidate_018(self): """ Test validate on a non-empty subversion section, non-empty repositoryDirs, collectMode only on repositoryDirs. """ repositoryDirs = [RepositoryDir(directoryPath="/two", collectMode="weekly")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.compressMode = "gzip" config.subversion.repositoryDirs = repositoryDirs config.validate() def testValidate_019(self): """ Test validate on a non-empty subversion section, non-empty repositoryDirs, compressMode only on repositoryDirs. """ repositoryDirs = [RepositoryDir(directoryPath="/two", compressMode="bzip2")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "weekly" config.subversion.repositoryDirs = repositoryDirs config.validate() def testValidate_020(self): """ Test validate on a non-empty subversion section, non-empty repositoryDirs, compressMode default and on repository. """ repositoryDirs = [RepositoryDir(directoryPath="/two", compressMode="bzip2")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "daily" config.subversion.compressMode = "gzip" config.subversion.repositoryDirs = repositoryDirs config.validate() def testValidate_021(self): """ Test validate on a non-empty subversion section, non-empty repositoryDirs, collectMode default and on repository. """ repositoryDirs = [RepositoryDir(directoryPath="/two", collectMode="daily")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "daily" config.subversion.compressMode = "gzip" config.subversion.repositoryDirs = repositoryDirs config.validate() def testValidate_022(self): """ Test validate on a non-empty subversion section, non-empty repositoryDirs, collectMode and compressMode default and on repository. """ repositoryDirs = [RepositoryDir(directoryPath="/two", collectMode="daily", compressMode="bzip2")] config = LocalConfig() config.subversion = SubversionConfig() config.subversion.collectMode = "daily" config.subversion.compressMode = "gzip" config.subversion.repositoryDirs = repositoryDirs config.validate() ############################ # Test parsing of documents ############################ def testParse_001(self): """ Parse empty config document. """ path = self.resources["subversion.conf.1"] with open(path) as f: contents = f.read() self.assertRaises(ValueError, LocalConfig, xmlPath=path, validate=True) self.assertRaises(ValueError, LocalConfig, xmlData=contents, validate=True) config = LocalConfig(xmlPath=path, validate=False) self.assertEqual(None, config.subversion) config = LocalConfig(xmlData=contents, validate=False) self.assertEqual(None, config.subversion) def testParse_002(self): """ Parse config document with default modes, one repository. """ repositories = [ Repository(repositoryPath="/opt/public/svn/software"), ] path = self.resources["subversion.conf.2"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual("daily", config.subversion.collectMode) self.assertEqual("gzip", config.subversion.compressMode) self.assertEqual(repositories, config.subversion.repositories) self.assertEqual(None, config.subversion.repositoryDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual("daily", config.subversion.collectMode) self.assertEqual("gzip", config.subversion.compressMode) self.assertEqual(repositories, config.subversion.repositories) self.assertEqual(None, config.subversion.repositoryDirs) def testParse_003(self): """ Parse config document with no default modes, one repository """ repositories = [ Repository(repositoryPath="/opt/public/svn/software", collectMode="daily", compressMode="gzip"), ] path = self.resources["subversion.conf.3"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual(None, config.subversion.collectMode) self.assertEqual(None, config.subversion.compressMode) self.assertEqual(repositories, config.subversion.repositories) self.assertEqual(None, config.subversion.repositoryDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual(None, config.subversion.collectMode) self.assertEqual(None, config.subversion.compressMode) self.assertEqual(repositories, config.subversion.repositories) self.assertEqual(None, config.subversion.repositoryDirs) def testParse_004(self): """ Parse config document with default modes, several repositories with various overrides. """ repositories = [] repositories.append(Repository(repositoryPath="/opt/public/svn/one")) repositories.append(Repository(repositoryType="BDB", repositoryPath="/opt/public/svn/two", collectMode="weekly")) repositories.append(Repository(repositoryPath="/opt/public/svn/three", compressMode="bzip2")) repositories.append( Repository(repositoryType="FSFS", repositoryPath="/opt/public/svn/four", collectMode="incr", compressMode="bzip2") ) path = self.resources["subversion.conf.4"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual("daily", config.subversion.collectMode) self.assertEqual("gzip", config.subversion.compressMode) self.assertEqual(repositories, config.subversion.repositories) self.assertEqual(None, config.subversion.repositoryDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual("daily", config.subversion.collectMode) self.assertEqual("gzip", config.subversion.compressMode) self.assertEqual(repositories, config.subversion.repositories) self.assertEqual(None, config.subversion.repositoryDirs) def testParse_005(self): """ Parse config document with default modes, one repository. """ repositoryDirs = [ RepositoryDir(directoryPath="/opt/public/svn/software"), ] path = self.resources["subversion.conf.5"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual("daily", config.subversion.collectMode) self.assertEqual("gzip", config.subversion.compressMode) self.assertEqual(None, config.subversion.repositories) self.assertEqual(repositoryDirs, config.subversion.repositoryDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual("daily", config.subversion.collectMode) self.assertEqual("gzip", config.subversion.compressMode) self.assertEqual(None, config.subversion.repositories) self.assertEqual(repositoryDirs, config.subversion.repositoryDirs) def testParse_006(self): """ Parse config document with no default modes, one repository """ repositoryDirs = [ RepositoryDir(directoryPath="/opt/public/svn/software", collectMode="daily", compressMode="gzip"), ] path = self.resources["subversion.conf.6"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual(None, config.subversion.collectMode) self.assertEqual(None, config.subversion.compressMode) self.assertEqual(None, config.subversion.repositories) self.assertEqual(repositoryDirs, config.subversion.repositoryDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual(None, config.subversion.collectMode) self.assertEqual(None, config.subversion.compressMode) self.assertEqual(None, config.subversion.repositories) self.assertEqual(repositoryDirs, config.subversion.repositoryDirs) def testParse_007(self): """ Parse config document with default modes, several repositoryDirs with various overrides. """ repositoryDirs = [] repositoryDirs.append(RepositoryDir(directoryPath="/opt/public/svn/one")) repositoryDirs.append( RepositoryDir( repositoryType="BDB", directoryPath="/opt/public/svn/two", collectMode="weekly", relativeExcludePaths=["software"] ) ) repositoryDirs.append( RepositoryDir(directoryPath="/opt/public/svn/three", compressMode="bzip2", excludePatterns=[".*software.*"]) ) repositoryDirs.append( RepositoryDir( repositoryType="FSFS", directoryPath="/opt/public/svn/four", collectMode="incr", compressMode="bzip2", relativeExcludePaths=["cedar", "banner"], excludePatterns=[".*software.*", ".*database.*"], ) ) path = self.resources["subversion.conf.7"] with open(path) as f: contents = f.read() config = LocalConfig(xmlPath=path, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual("daily", config.subversion.collectMode) self.assertEqual("gzip", config.subversion.compressMode) self.assertEqual(None, config.subversion.repositories) self.assertEqual(repositoryDirs, config.subversion.repositoryDirs) config = LocalConfig(xmlData=contents, validate=False) self.assertNotEqual(None, config.subversion) self.assertEqual("daily", config.subversion.collectMode) self.assertEqual("gzip", config.subversion.compressMode) self.assertEqual(None, config.subversion.repositories) self.assertEqual(repositoryDirs, config.subversion.repositoryDirs) ################### # Test addConfig() ################### def testAddConfig_001(self): """ Test with empty config document. """ subversion = SubversionConfig() config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_002(self): """ Test with defaults set, single repository with no optional values. """ repositories = [] repositories.append(Repository(repositoryPath="/path")) subversion = SubversionConfig(collectMode="daily", compressMode="gzip", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_003(self): """ Test with defaults set, single repository with collectMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path", collectMode="incr")) subversion = SubversionConfig(collectMode="daily", compressMode="gzip", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_004(self): """ Test with defaults set, single repository with compressMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path", compressMode="bzip2")) subversion = SubversionConfig(collectMode="daily", compressMode="gzip", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_005(self): """ Test with defaults set, single repository with collectMode and compressMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path", collectMode="weekly", compressMode="bzip2")) subversion = SubversionConfig(collectMode="daily", compressMode="gzip", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_006(self): """ Test with no defaults set, single repository with collectMode and compressMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path", collectMode="weekly", compressMode="bzip2")) subversion = SubversionConfig(repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_007(self): """ Test with compressMode set, single repository with collectMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path", collectMode="weekly")) subversion = SubversionConfig(compressMode="gzip", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_008(self): """ Test with collectMode set, single repository with compressMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path", compressMode="gzip")) subversion = SubversionConfig(collectMode="weekly", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_009(self): """ Test with compressMode set, single repository with collectMode and compressMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path", collectMode="incr", compressMode="gzip")) subversion = SubversionConfig(compressMode="bzip2", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_010(self): """ Test with collectMode set, single repository with collectMode and compressMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path", collectMode="weekly", compressMode="gzip")) subversion = SubversionConfig(collectMode="incr", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) def testAddConfig_011(self): """ Test with defaults set, multiple repositories with collectMode and compressMode set. """ repositories = [] repositories.append(Repository(repositoryPath="/path1", collectMode="daily", compressMode="gzip")) repositories.append(Repository(repositoryPath="/path2", collectMode="weekly", compressMode="gzip")) repositories.append(Repository(repositoryPath="/path3", collectMode="incr", compressMode="gzip")) repositories.append(Repository(repositoryPath="/path1", collectMode="daily", compressMode="bzip2")) repositories.append(Repository(repositoryPath="/path2", collectMode="weekly", compressMode="bzip2")) repositories.append(Repository(repositoryPath="/path3", collectMode="incr", compressMode="bzip2")) subversion = SubversionConfig(collectMode="incr", compressMode="bzip2", repositories=repositories) config = LocalConfig() config.subversion = subversion self.validateAddConfig(config) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1649563 cedar_backup3-3.8.1/tests/test_sync.py0000644000000000000000000045367514567004737014727 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2007,2010,2015,2020 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests Amazon S3 sync tool functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/tools/amazons3.py. Code Coverage ============= This module contains individual tests for the many of the public functions and classes implemented in tools/amazons3.py. Where possible, we test functions that print output by passing a custom file descriptor. Sometimes, we only ensure that a function or method runs without failure, and we don't validate what its result is or what it prints out. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a SYNCTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import unittest from getopt import GetoptError from CedarBackup3.testutil import captureOutput, configureLogging, failUnlessAssignRaises from CedarBackup3.tools.amazons3 import Options, _usage, _version ####################################################################### # Test Case Classes ####################################################################### ###################### # TestFunctions class ###################### class TestFunctions(unittest.TestCase): """Tests for the public functions.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ######################## # Test simple functions ######################## def testSimpleFuncs_001(self): """ Test that the _usage() function runs without errors. We don't care what the output is, and we don't check. """ captureOutput(_usage) def testSimpleFuncs_002(self): """ Test that the _version() function runs without errors. We don't care what the output is, and we don't check. """ captureOutput(_version) #################### # TestOptions class #################### class TestOptions(unittest.TestCase): """Tests for the Options class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ################## # Utility methods ################## def failUnlessAssignRaises(self, exception, obj, prop, value): """Equivalent of :any:`failUnlessRaises`, but used for property assignments instead.""" failUnlessAssignRaises(self, exception, obj, prop, value) ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = Options() obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with no arguments. """ options = Options() self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_002(self): """ Test constructor with validate=False, no other arguments. """ options = Options(validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_003(self): """ Test constructor with argumentList=[], validate=False. """ options = Options(argumentList=[], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_004(self): """ Test constructor with argumentString="", validate=False. """ options = Options(argumentString="", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_005(self): """ Test constructor with argumentList=["--help", ], validate=False. """ options = Options(argumentList=["--help"], validate=False) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_006(self): """ Test constructor with argumentString="--help", validate=False. """ options = Options(argumentString="--help", validate=False) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_007(self): """ Test constructor with argumentList=["-h", ], validate=False. """ options = Options(argumentList=["-h"], validate=False) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_008(self): """ Test constructor with argumentString="-h", validate=False. """ options = Options(argumentString="-h", validate=False) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_009(self): """ Test constructor with argumentList=["--version", ], validate=False. """ options = Options(argumentList=["--version"], validate=False) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_010(self): """ Test constructor with argumentString="--version", validate=False. """ options = Options(argumentString="--version", validate=False) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_011(self): """ Test constructor with argumentList=["-V", ], validate=False. """ options = Options(argumentList=["-V"], validate=False) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_012(self): """ Test constructor with argumentString="-V", validate=False. """ options = Options(argumentString="-V", validate=False) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_013(self): """ Test constructor with argumentList=["--verbose", ], validate=False. """ options = Options(argumentList=["--verbose"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_014(self): """ Test constructor with argumentString="--verbose", validate=False. """ options = Options(argumentString="--verbose", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_015(self): """ Test constructor with argumentList=["-b", ], validate=False. """ options = Options(argumentList=["-b"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_016(self): """ Test constructor with argumentString="-b", validate=False. """ options = Options(argumentString="-b", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_017(self): """ Test constructor with argumentList=["--quiet", ], validate=False. """ options = Options(argumentList=["--quiet"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(True, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_018(self): """ Test constructor with argumentString="--quiet", validate=False. """ options = Options(argumentString="--quiet", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(True, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_019(self): """ Test constructor with argumentList=["-q", ], validate=False. """ options = Options(argumentList=["-q"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(True, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_020(self): """ Test constructor with argumentString="-q", validate=False. """ options = Options(argumentString="-q", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(True, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_021(self): """ Test constructor with argumentList=["--logfile", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["--logfile"], validate=False) def testConstructor_022(self): """ Test constructor with argumentString="--logfile", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="--logfile", validate=False) def testConstructor_023(self): """ Test constructor with argumentList=["-l", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["-l"], validate=False) def testConstructor_024(self): """ Test constructor with argumentString="-l", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="-l", validate=False) def testConstructor_025(self): """ Test constructor with argumentList=["--logfile", "something", ], validate=False. """ options = Options(argumentList=["--logfile", "something"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual("something", options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_026(self): """ Test constructor with argumentString="--logfile something", validate=False. """ options = Options(argumentString="--logfile something", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual("something", options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_027(self): """ Test constructor with argumentList=["-l", "something", ], validate=False. """ options = Options(argumentList=["-l", "something"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual("something", options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_028(self): """ Test constructor with argumentString="-l something", validate=False. """ options = Options(argumentString="-l something", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual("something", options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_029(self): """ Test constructor with argumentList=["--owner", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["--owner"], validate=False) def testConstructor_030(self): """ Test constructor with argumentString="--owner", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="--owner", validate=False) def testConstructor_040(self): """ Test constructor with argumentList=["-o", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["-o"], validate=False) def testConstructor_041(self): """ Test constructor with argumentString="-o", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="-o", validate=False) def testConstructor_042(self): """ Test constructor with argumentList=["--owner", "something", ], validate=False. """ self.assertRaises(ValueError, Options, argumentList=["--owner", "something"], validate=False) def testConstructor_043(self): """ Test constructor with argumentString="--owner something", validate=False. """ self.assertRaises(ValueError, Options, argumentString="--owner something", validate=False) def testConstructor_044(self): """ Test constructor with argumentList=["-o", "something", ], validate=False. """ self.assertRaises(ValueError, Options, argumentList=["-o", "something"], validate=False) def testConstructor_045(self): """ Test constructor with argumentString="-o something", validate=False. """ self.assertRaises(ValueError, Options, argumentString="-o something", validate=False) def testConstructor_046(self): """ Test constructor with argumentList=["--owner", "a:b", ], validate=False. """ options = Options(argumentList=["--owner", "a:b"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(("a", "b"), options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_047(self): """ Test constructor with argumentString="--owner a:b", validate=False. """ options = Options(argumentString="--owner a:b", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(("a", "b"), options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_048(self): """ Test constructor with argumentList=["-o", "a:b", ], validate=False. """ options = Options(argumentList=["-o", "a:b"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(("a", "b"), options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_049(self): """ Test constructor with argumentString="-o a:b", validate=False. """ options = Options(argumentString="-o a:b", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(("a", "b"), options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_050(self): """ Test constructor with argumentList=["--mode", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["--mode"], validate=False) def testConstructor_051(self): """ Test constructor with argumentString="--mode", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="--mode", validate=False) def testConstructor_052(self): """ Test constructor with argumentList=["-m", ], validate=False. """ self.assertRaises(GetoptError, Options, argumentList=["-m"], validate=False) def testConstructor_053(self): """ Test constructor with argumentString="-m", validate=False. """ self.assertRaises(GetoptError, Options, argumentString="-m", validate=False) def testConstructor_054(self): """ Test constructor with argumentList=["--mode", "something", ], validate=False. """ self.assertRaises(ValueError, Options, argumentList=["--mode", "something"], validate=False) def testConstructor_055(self): """ Test constructor with argumentString="--mode something", validate=False. """ self.assertRaises(ValueError, Options, argumentString="--mode something", validate=False) def testConstructor_056(self): """ Test constructor with argumentList=["-m", "something", ], validate=False. """ self.assertRaises(ValueError, Options, argumentList=["-m", "something"], validate=False) def testConstructor_057(self): """ Test constructor with argumentString="-m something", validate=False. """ self.assertRaises(ValueError, Options, argumentString="-m something", validate=False) def testConstructor_058(self): """ Test constructor with argumentList=["--mode", "631", ], validate=False. """ options = Options(argumentList=["--mode", "631"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o631, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_059(self): """ Test constructor with argumentString="--mode 631", validate=False. """ options = Options(argumentString="--mode 631", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o631, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_060(self): """ Test constructor with argumentList=["-m", "631", ], validate=False. """ options = Options(argumentList=["-m", "631"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o631, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_061(self): """ Test constructor with argumentString="-m 631", validate=False. """ options = Options(argumentString="-m 631", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o631, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_062(self): """ Test constructor with argumentList=["--output", ], validate=False. """ options = Options(argumentList=["--output"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(True, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_063(self): """ Test constructor with argumentString="--output", validate=False. """ options = Options(argumentString="--output", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(True, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_064(self): """ Test constructor with argumentList=["-O", ], validate=False. """ options = Options(argumentList=["-O"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(True, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_065(self): """ Test constructor with argumentString="-O", validate=False. """ options = Options(argumentString="-O", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(True, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_066(self): """ Test constructor with argumentList=["--debug", ], validate=False. """ options = Options(argumentList=["--debug"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_067(self): """ Test constructor with argumentString="--debug", validate=False. """ options = Options(argumentString="--debug", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_068(self): """ Test constructor with argumentList=["-d", ], validate=False. """ options = Options(argumentList=["-d"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_069(self): """ Test constructor with argumentString="-d", validate=False. """ options = Options(argumentString="-d", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_070(self): """ Test constructor with argumentList=["--stack", ], validate=False. """ options = Options(argumentList=["--stack"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(True, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_071(self): """ Test constructor with argumentString="--stack", validate=False. """ options = Options(argumentString="--stack", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(True, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_072(self): """ Test constructor with argumentList=["-s", ], validate=False. """ options = Options(argumentList=["-s"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(True, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_073(self): """ Test constructor with argumentString="-s", validate=False. """ options = Options(argumentString="-s", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(True, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_074(self): """ Test constructor with argumentList=["--diagnostics", ], validate=False. """ options = Options(argumentList=["--diagnostics"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_075(self): """ Test constructor with argumentString="--diagnostics", validate=False. """ options = Options(argumentString="--diagnostics", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_076(self): """ Test constructor with argumentList=["-D", ], validate=False. """ options = Options(argumentList=["-D"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_077(self): """ Test constructor with argumentString="-D", validate=False. """ options = Options(argumentString="-D", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_078(self): """ Test constructor with argumentList=["--verifyOnly", ], validate=False. """ options = Options(argumentList=["--verifyOnly"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(True, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_079(self): """ Test constructor with argumentString="--verifyOnly", validate=False. """ options = Options(argumentString="--verifyOnly", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(True, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_080(self): """ Test constructor with argumentList=["-v", ], validate=False. """ options = Options(argumentList=["-v"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(True, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_081(self): """ Test constructor with argumentString="-v", validate=False. """ options = Options(argumentString="-v", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(True, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_082(self): """ Test constructor with argumentList=["--ignoreWarnings", ], validate=False. """ options = Options(argumentList=["--ignoreWarnings"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(True, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_083(self): """ Test constructor with argumentString="--ignoreWarnings", validate=False. """ options = Options(argumentString="--ignoreWarnings", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(True, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_084(self): """ Test constructor with argumentList=["-w", ], validate=False. """ options = Options(argumentList=["-w"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(True, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_085(self): """ Test constructor with argumentString="-w", validate=False. """ options = Options(argumentString="-w", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(True, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_086(self): """ Test constructor with argumentList=["source", "bucket", ], validate=False. """ options = Options(argumentList=["source", "bucket"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_087(self): """ Test constructor with argumentString="source bucket", validate=False. """ options = Options(argumentString="source bucket", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_088(self): """ Test constructor with argumentList=["-d", "--verbose", "-O", "--mode", "600", "source", "bucket", ], validate=False. """ options = Options(argumentList=["-d", "--verbose", "-O", "--mode", "600", "source", "bucket"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o600, options.mode) self.assertEqual(True, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_089(self): """ Test constructor with argumentString="-d --verbose -O --mode 600 source bucket", validate=False. """ options = Options(argumentString="-d --verbose -O --mode 600 source bucket", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o600, options.mode) self.assertEqual(True, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_090(self): """ Test constructor with argumentList=[], validate=True. """ self.assertRaises(ValueError, Options, argumentList=[], validate=True) def testConstructor_091(self): """ Test constructor with argumentString="", validate=True. """ self.assertRaises(ValueError, Options, argumentString="", validate=True) def testConstructor_092(self): """ Test constructor with argumentList=["--help", ], validate=True. """ options = Options(argumentList=["--help"], validate=True) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_093(self): """ Test constructor with argumentString="--help", validate=True. """ options = Options(argumentString="--help", validate=True) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_094(self): """ Test constructor with argumentList=["-h", ], validate=True. """ options = Options(argumentList=["-h"], validate=True) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_095(self): """ Test constructor with argumentString="-h", validate=True. """ options = Options(argumentString="-h", validate=True) self.assertEqual(True, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_096(self): """ Test constructor with argumentList=["--version", ], validate=True. """ options = Options(argumentList=["--version"], validate=True) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_097(self): """ Test constructor with argumentString="--version", validate=True. """ options = Options(argumentString="--version", validate=True) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_098(self): """ Test constructor with argumentList=["-V", ], validate=True. """ options = Options(argumentList=["-V"], validate=True) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_099(self): """ Test constructor with argumentString="-V", validate=True. """ options = Options(argumentString="-V", validate=True) self.assertEqual(False, options.help) self.assertEqual(True, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_100(self): """ Test constructor with argumentList=["--verbose", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--verbose"], validate=True) def testConstructor_101(self): """ Test constructor with argumentString="--verbose", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--verbose", validate=True) def testConstructor_102(self): """ Test constructor with argumentList=["-b", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-b"], validate=True) def testConstructor_103(self): """ Test constructor with argumentString="-b", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-b", validate=True) def testConstructor_104(self): """ Test constructor with argumentList=["--quiet", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--quiet"], validate=True) def testConstructor_105(self): """ Test constructor with argumentString="--quiet", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--quiet", validate=True) def testConstructor_106(self): """ Test constructor with argumentList=["-q", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-q"], validate=True) def testConstructor_107(self): """ Test constructor with argumentString="-q", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-q", validate=True) def testConstructor_108(self): """ Test constructor with argumentList=["--logfile", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["--logfile"], validate=True) def testConstructor_109(self): """ Test constructor with argumentString="--logfile", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="--logfile", validate=True) def testConstructor_110(self): """ Test constructor with argumentList=["-l", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["-l"], validate=True) def testConstructor_111(self): """ Test constructor with argumentString="-l", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="-l", validate=True) def testConstructor_112(self): """ Test constructor with argumentList=["--logfile", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--logfile", "something"], validate=True) def testConstructor_113(self): """ Test constructor with argumentString="--logfile something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--logfile something", validate=True) def testConstructor_114(self): """ Test constructor with argumentList=["-l", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-l", "something"], validate=True) def testConstructor_115(self): """ Test constructor with argumentString="-l something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-l something", validate=True) def testConstructor_116(self): """ Test constructor with argumentList=["--owner", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["--owner"], validate=True) def testConstructor_117(self): """ Test constructor with argumentString="--owner", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="--owner", validate=True) def testConstructor_118(self): """ Test constructor with argumentList=["-o", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["-o"], validate=True) def testConstructor_119(self): """ Test constructor with argumentString="-o", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="-o", validate=True) def testConstructor_120(self): """ Test constructor with argumentList=["--owner", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--owner", "something"], validate=True) def testConstructor_121(self): """ Test constructor with argumentString="--owner something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--owner something", validate=True) def testConstructor_122(self): """ Test constructor with argumentList=["-o", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-o", "something"], validate=True) def testConstructor_123(self): """ Test constructor with argumentString="-o something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-o something", validate=True) def testConstructor_124(self): """ Test constructor with argumentList=["--owner", "a:b", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--owner", "a:b"], validate=True) def testConstructor_125(self): """ Test constructor with argumentString="--owner a:b", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--owner a:b", validate=True) def testConstructor_126(self): """ Test constructor with argumentList=["-o", "a:b", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-o", "a:b"], validate=True) def testConstructor_127(self): """ Test constructor with argumentString="-o a:b", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-o a:b", validate=True) def testConstructor_128(self): """ Test constructor with argumentList=["--mode", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["--mode"], validate=True) def testConstructor_129(self): """ Test constructor with argumentString="--mode", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="--mode", validate=True) def testConstructor_130(self): """ Test constructor with argumentList=["-m", ], validate=True. """ self.assertRaises(GetoptError, Options, argumentList=["-m"], validate=True) def testConstructor_131(self): """ Test constructor with argumentString="-m", validate=True. """ self.assertRaises(GetoptError, Options, argumentString="-m", validate=True) def testConstructor_132(self): """ Test constructor with argumentList=["--mode", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--mode", "something"], validate=True) def testConstructor_133(self): """ Test constructor with argumentString="--mode something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--mode something", validate=True) def testConstructor_134(self): """ Test constructor with argumentList=["-m", "something", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-m", "something"], validate=True) def testConstructor_135(self): """ Test constructor with argumentString="-m something", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-m something", validate=True) def testConstructor_136(self): """ Test constructor with argumentList=["--mode", "631", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--mode", "631"], validate=True) def testConstructor_137(self): """ Test constructor with argumentString="--mode 631", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--mode 631", validate=True) def testConstructor_138(self): """ Test constructor with argumentList=["-m", "631", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-m", "631"], validate=True) def testConstructor_139(self): """ Test constructor with argumentString="-m 631", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-m 631", validate=True) def testConstructor_140(self): """ Test constructor with argumentList=["--output", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--output"], validate=True) def testConstructor_141(self): """ Test constructor with argumentString="--output", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--output", validate=True) def testConstructor_142(self): """ Test constructor with argumentList=["-O", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-O"], validate=True) def testConstructor_143(self): """ Test constructor with argumentString="-O", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-O", validate=True) def testConstructor_144(self): """ Test constructor with argumentList=["--debug", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--debug"], validate=True) def testConstructor_145(self): """ Test constructor with argumentString="--debug", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--debug", validate=True) def testConstructor_146(self): """ Test constructor with argumentList=["-d", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-d"], validate=True) def testConstructor_147(self): """ Test constructor with argumentString="-d", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-d", validate=True) def testConstructor_148(self): """ Test constructor with argumentList=["--stack", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--stack"], validate=True) def testConstructor_149(self): """ Test constructor with argumentString="--stack", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--stack", validate=True) def testConstructor_150(self): """ Test constructor with argumentList=["-s", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-s"], validate=True) def testConstructor_151(self): """ Test constructor with argumentString="-s", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-s", validate=True) def testConstructor_152(self): """ Test constructor with argumentList=["--diagnostics", ], validate=True. """ options = Options(argumentList=["--diagnostics"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_153(self): """ Test constructor with argumentString="--diagnostics", validate=True. """ options = Options(argumentString="--diagnostics", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_154(self): """ Test constructor with argumentList=["-D", ], validate=True. """ options = Options(argumentList=["-D"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_155(self): """ Test constructor with argumentString="-D", validate=True. """ options = Options(argumentString="-D", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(True, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_156(self): """ Test constructor with argumentList=["--verifyOnly", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--verifyOnly"], validate=True) def testConstructor_157(self): """ Test constructor with argumentString="--verifyOnly", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--verifyOnly", validate=True) def testConstructor_158(self): """ Test constructor with argumentList=["-v", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-v"], validate=True) def testConstructor_159(self): """ Test constructor with argumentString="-v", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-v", validate=True) def testConstructor_160(self): """ Test constructor with argumentList=["--ignoreWarnings", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["--ignoreWarnings"], validate=True) def testConstructor_161(self): """ Test constructor with argumentString="--ignoreWarnings", validate=True. """ self.assertRaises(ValueError, Options, argumentString="--ignoreWarnings", validate=True) def testConstructor_162(self): """ Test constructor with argumentList=["-w", ], validate=True. """ self.assertRaises(ValueError, Options, argumentList=["-w"], validate=True) def testConstructor_163(self): """ Test constructor with argumentString="-w", validate=True. """ self.assertRaises(ValueError, Options, argumentString="-w", validate=True) def testConstructor_164(self): """ Test constructor with argumentList=["source", "bucket", ], validate=True. """ options = Options(argumentList=["source", "bucket"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_165(self): """ Test constructor with argumentString="source bucket", validate=True. """ options = Options(argumentString="source bucket", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_166(self): """ Test constructor with argumentList=["source", "bucket", ], validate=True. """ options = Options(argumentList=["source", "bucket"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_167(self): """ Test constructor with argumentString="source bucket", validate=True. """ options = Options(argumentString="source bucket", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_168(self): """ Test constructor with argumentList=["-d", "--verbose", "-O", "--mode", "600", "source", "bucket", ], validate=True. """ options = Options(argumentList=["-d", "--verbose", "-O", "--mode", "600", "source", "bucket"], validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o600, options.mode) self.assertEqual(True, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_169(self): """ Test constructor with argumentString="-d --verbose -O --mode 600 source bucket", validate=True. """ options = Options(argumentString="-d --verbose -O --mode 600 source bucket", validate=True) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(True, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(0o600, options.mode) self.assertEqual(True, options.output) self.assertEqual(True, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(False, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual("source", options.sourceDir) self.assertEqual("bucket", options.s3BucketUrl) def testConstructor_170(self): """ Test constructor with argumentList=["--uploadOnly", ], validate=False. """ options = Options(argumentList=["--uploadOnly"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(True, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_171(self): """ Test constructor with argumentString="--verifyOnly", validate=False. """ options = Options(argumentString="--uploadOnly", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(True, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_172(self): """ Test constructor with argumentList=["-u", ], validate=False. """ options = Options(argumentList=["-u"], validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(True, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) def testConstructor_173(self): """ Test constructor with argumentString="-u", validate=False. """ options = Options(argumentString="-u", validate=False) self.assertEqual(False, options.help) self.assertEqual(False, options.version) self.assertEqual(False, options.verbose) self.assertEqual(False, options.quiet) self.assertEqual(None, options.logfile) self.assertEqual(None, options.owner) self.assertEqual(None, options.mode) self.assertEqual(False, options.output) self.assertEqual(False, options.debug) self.assertEqual(False, options.stacktrace) self.assertEqual(False, options.diagnostics) self.assertEqual(False, options.verifyOnly) self.assertEqual(True, options.uploadOnly) self.assertEqual(False, options.ignoreWarnings) self.assertEqual(None, options.sourceDir) self.assertEqual(None, options.s3BucketUrl) ############################ # Test comparison operators ############################ def testComparison_001(self): """ Test comparison of two identical objects, all attributes at defaults. """ options1 = Options() options2 = Options() self.assertEqual(options1, options2) self.assertTrue(options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(not options1 != options2) def testComparison_002(self): """ Test comparison of two identical objects, all attributes filled in and same. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertEqual(options1, options2) self.assertTrue(options1 == options2) self.assertTrue(not options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(options1 >= options2) self.assertTrue(not options1 != options2) def testComparison_003(self): """ Test comparison of two identical objects, all attributes filled in, help different. """ options1 = Options() options2 = Options() options1.help = False options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_004(self): """ Test comparison of two identical objects, all attributes filled in, version different. """ options1 = Options() options2 = Options() options1.help = True options1.version = False options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_005(self): """ Test comparison of two identical objects, all attributes filled in, verbose different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = False options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_006(self): """ Test comparison of two identical objects, all attributes filled in, quiet different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = False options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_007(self): """ Test comparison of two identical objects, all attributes filled in, logfile different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = None options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_008(self): """ Test comparison of two identical objects, all attributes filled in, owner different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = None options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_009(self): """ Test comparison of two identical objects, all attributes filled in, mode different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = None options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_010(self): """ Test comparison of two identical objects, all attributes filled in, output different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = False options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_011(self): """ Test comparison of two identical objects, all attributes filled in, debug different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = False options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_012(self): """ Test comparison of two identical objects, all attributes filled in, stacktrace different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = False options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_013(self): """ Test comparison of two identical objects, all attributes filled in, diagnostics different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = False options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_014(self): """ Test comparison of two identical objects, all attributes filled in, verifyOnly different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = False options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_015(self): """ Test comparison of two identical objects, all attributes filled in, sourceDir different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = None options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_016(self): """ Test comparison of two identical objects, all attributes filled in, s3BucketUrl different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = True options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = None options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) def testComparison_017(self): """ Test comparison of two identical objects, all attributes filled in, uploadOnly different. """ options1 = Options() options2 = Options() options1.help = True options1.version = True options1.verbose = True options1.quiet = True options1.logfile = "logfile" options1.owner = ("a", "b") options1.mode = "631" options1.output = True options1.debug = True options1.stacktrace = True options1.diagnostics = True options1.verifyOnly = True options1.uploadOnly = False options1.ignoreWarnings = True options1.sourceDir = "source" options1.s3BucketUrl = "bucket" options2.help = True options2.version = True options2.verbose = True options2.quiet = True options2.logfile = "logfile" options2.owner = ("a", "b") options2.mode = "631" options2.output = True options2.debug = True options2.stacktrace = True options2.diagnostics = True options2.verifyOnly = True options2.uploadOnly = True options2.ignoreWarnings = True options2.sourceDir = "source" options2.s3BucketUrl = "bucket" self.assertNotEqual(options1, options2) self.assertTrue(not options1 == options2) self.assertTrue(options1 < options2) self.assertTrue(options1 <= options2) self.assertTrue(not options1 > options2) self.assertTrue(not options1 >= options2) self.assertTrue(options1 != options2) ########################### # Test buildArgumentList() ########################### def testBuildArgumentList_001(self): """Test with no values set, validate=False.""" options = Options() argumentList = options.buildArgumentList(validate=False) self.assertEqual([], argumentList) def testBuildArgumentList_002(self): """Test with help set, validate=False.""" options = Options() options.help = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--help"], argumentList) def testBuildArgumentList_003(self): """Test with version set, validate=False.""" options = Options() options.version = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--version"], argumentList) def testBuildArgumentList_004(self): """Test with verbose set, validate=False.""" options = Options() options.verbose = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--verbose"], argumentList) def testBuildArgumentList_005(self): """Test with quiet set, validate=False.""" options = Options() options.quiet = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--quiet"], argumentList) def testBuildArgumentList_006(self): """Test with logfile set, validate=False.""" options = Options() options.logfile = "bogus" argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--logfile", "bogus"], argumentList) def testBuildArgumentList_007(self): """Test with owner set, validate=False.""" options = Options() options.owner = ("ken", "group") argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--owner", "ken:group"], argumentList) def testBuildArgumentList_008(self): """Test with mode set, validate=False.""" options = Options() options.mode = 0o644 argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--mode", "644"], argumentList) def testBuildArgumentList_009(self): """Test with output set, validate=False.""" options = Options() options.output = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--output"], argumentList) def testBuildArgumentList_010(self): """Test with debug set, validate=False.""" options = Options() options.debug = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--debug"], argumentList) def testBuildArgumentList_011(self): """Test with stacktrace set, validate=False.""" options = Options() options.stacktrace = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--stack"], argumentList) def testBuildArgumentList_012(self): """Test with diagnostics set, validate=False.""" options = Options() options.diagnostics = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--diagnostics"], argumentList) def testBuildArgumentList_013(self): """Test with verifyOnly set, validate=False.""" options = Options() options.verifyOnly = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--verifyOnly"], argumentList) def testBuildArgumentList_014(self): """Test with ignoreWarnings set, validate=False.""" options = Options() options.ignoreWarnings = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--ignoreWarnings"], argumentList) def testBuildArgumentList_015(self): """Test with valid source and target, validate=False.""" options = Options() options.sourceDir = "source" options.s3BucketUrl = "bucket" argumentList = options.buildArgumentList(validate=False) self.assertEqual(["source", "bucket"], argumentList) def testBuildArgumentList_016(self): """Test with all values set, actions containing one item, validate=False.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.verifyOnly = True options.ignoreWarnings = True options.sourceDir = "source" options.s3BucketUrl = "bucket" argumentList = options.buildArgumentList(validate=False) self.assertEqual( [ "--help", "--version", "--verbose", "--quiet", "--logfile", "logfile", "--owner", "a:b", "--mode", "631", "--output", "--debug", "--stack", "--diagnostics", "--verifyOnly", "--ignoreWarnings", "source", "bucket", ], argumentList, ) def testBuildArgumentList_017(self): """Test with no values set, validate=True.""" options = Options() self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_018(self): """Test with help set, validate=True.""" options = Options() options.help = True argumentList = options.buildArgumentList(validate=True) self.assertEqual(["--help"], argumentList) def testBuildArgumentList_019(self): """Test with version set, validate=True.""" options = Options() options.version = True argumentList = options.buildArgumentList(validate=True) self.assertEqual(["--version"], argumentList) def testBuildArgumentList_020(self): """Test with verbose set, validate=True.""" options = Options() options.verbose = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_021(self): """Test with quiet set, validate=True.""" options = Options() options.quiet = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_022(self): """Test with logfile set, validate=True.""" options = Options() options.logfile = "bogus" self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_023(self): """Test with owner set, validate=True.""" options = Options() options.owner = ("ken", "group") self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_024(self): """Test with mode set, validate=True.""" options = Options() options.mode = 0o644 self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_025(self): """Test with output set, validate=True.""" options = Options() options.output = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_026(self): """Test with debug set, validate=True.""" options = Options() options.debug = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_027(self): """Test with stacktrace set, validate=True.""" options = Options() options.stacktrace = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_028(self): """Test with diagnostics set, validate=True.""" options = Options() options.diagnostics = True argumentList = options.buildArgumentList(validate=True) self.assertEqual(["--diagnostics"], argumentList) def testBuildArgumentList_029(self): """Test with verifyOnly set, validate=True.""" options = Options() options.verifyOnly = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_030(self): """Test with ignoreWarnings set, validate=True.""" options = Options() options.ignoreWarnings = True self.assertRaises(ValueError, options.buildArgumentList, validate=True) def testBuildArgumentList_031(self): """Test with valid source and target, validate=True.""" options = Options() options.sourceDir = "source" options.s3BucketUrl = "bucket" argumentList = options.buildArgumentList(validate=True) self.assertEqual(["source", "bucket"], argumentList) def testBuildArgumentList_032(self): """Test with all values set (except managed ones), actions containing one item, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.verifyOnly = True options.ignoreWarnings = True options.sourceDir = "source" options.s3BucketUrl = "bucket" argumentList = options.buildArgumentList(validate=True) self.assertEqual( [ "--help", "--version", "--verbose", "--quiet", "--logfile", "logfile", "--owner", "a:b", "--mode", "631", "--output", "--debug", "--stack", "--diagnostics", "--verifyOnly", "--ignoreWarnings", "source", "bucket", ], argumentList, ) def testBuildArgumentList_033(self): """Test with uploadOnly set, validate=False.""" options = Options() options.uploadOnly = True argumentList = options.buildArgumentList(validate=False) self.assertEqual(["--uploadOnly"], argumentList) ############################# # Test buildArgumentString() ############################# def testBuildArgumentString_001(self): """Test with no values set, validate=False.""" options = Options() argumentString = options.buildArgumentString(validate=False) self.assertEqual("", argumentString) def testBuildArgumentString_002(self): """Test with help set, validate=False.""" options = Options() options.help = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--help ", argumentString) def testBuildArgumentString_003(self): """Test with version set, validate=False.""" options = Options() options.version = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--version ", argumentString) def testBuildArgumentString_004(self): """Test with verbose set, validate=False.""" options = Options() options.verbose = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--verbose ", argumentString) def testBuildArgumentString_005(self): """Test with quiet set, validate=False.""" options = Options() options.quiet = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--quiet ", argumentString) def testBuildArgumentString_006(self): """Test with logfile set, validate=False.""" options = Options() options.logfile = "bogus" argumentString = options.buildArgumentString(validate=False) self.assertEqual('--logfile "bogus" ', argumentString) def testBuildArgumentString_007(self): """Test with owner set, validate=False.""" options = Options() options.owner = ("ken", "group") argumentString = options.buildArgumentString(validate=False) self.assertEqual('--owner "ken:group" ', argumentString) def testBuildArgumentString_008(self): """Test with mode set, validate=False.""" options = Options() options.mode = 0o644 argumentString = options.buildArgumentString(validate=False) self.assertEqual("--mode 644 ", argumentString) def testBuildArgumentString_009(self): """Test with output set, validate=False.""" options = Options() options.output = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--output ", argumentString) def testBuildArgumentString_010(self): """Test with debug set, validate=False.""" options = Options() options.debug = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--debug ", argumentString) def testBuildArgumentString_011(self): """Test with stacktrace set, validate=False.""" options = Options() options.stacktrace = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--stack ", argumentString) def testBuildArgumentString_012(self): """Test with diagnostics set, validate=False.""" options = Options() options.diagnostics = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--diagnostics ", argumentString) def testBuildArgumentString_013(self): """Test with verifyOnly set, validate=False.""" options = Options() options.verifyOnly = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--verifyOnly ", argumentString) def testBuildArgumentString_014(self): """Test with ignoreWarnings set, validate=False.""" options = Options() options.ignoreWarnings = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--ignoreWarnings ", argumentString) def testBuildArgumentString_015(self): """Test with valid source and target, validate=False.""" options = Options() options.sourceDir = "source" options.s3BucketUrl = "bucket" argumentString = options.buildArgumentString(validate=False) self.assertEqual('"source" "bucket" ', argumentString) def testBuildArgumentString_016(self): """Test with all values set, actions containing one item, validate=False.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.verifyOnly = True options.ignoreWarnings = True options.sourceDir = "source" options.s3BucketUrl = "bucket" argumentString = options.buildArgumentString(validate=False) self.assertEqual( '--help --version --verbose --quiet --logfile "logfile" --owner "a:b" --mode 631 --output --debug --stack --diagnostics --verifyOnly --ignoreWarnings "source" "bucket" ', argumentString, ) def testBuildArgumentString_017(self): """Test with no values set, validate=True.""" options = Options() self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_018(self): """Test with help set, validate=True.""" options = Options() options.help = True argumentString = options.buildArgumentString(validate=True) self.assertEqual("--help ", argumentString) def testBuildArgumentString_019(self): """Test with version set, validate=True.""" options = Options() options.version = True argumentString = options.buildArgumentString(validate=True) self.assertEqual("--version ", argumentString) def testBuildArgumentString_020(self): """Test with verbose set, validate=True.""" options = Options() options.verbose = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_021(self): """Test with quiet set, validate=True.""" options = Options() options.quiet = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_022(self): """Test with logfile set, validate=True.""" options = Options() options.logfile = "bogus" self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_023(self): """Test with owner set, validate=True.""" options = Options() options.owner = ("ken", "group") self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_024(self): """Test with mode set, validate=True.""" options = Options() options.mode = 0o644 self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_025(self): """Test with output set, validate=True.""" options = Options() options.output = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_026(self): """Test with debug set, validate=True.""" options = Options() options.debug = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_027(self): """Test with stacktrace set, validate=True.""" options = Options() options.stacktrace = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_028(self): """Test with diagnostics set, validate=True.""" options = Options() options.diagnostics = True argumentString = options.buildArgumentString(validate=True) self.assertEqual("--diagnostics ", argumentString) def testBuildArgumentString_029(self): """Test with verifyOnly set, validate=True.""" options = Options() options.verifyOnly = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_030(self): """Test with ignoreWarnings set, validate=True.""" options = Options() options.ignoreWarnings = True self.assertRaises(ValueError, options.buildArgumentString, validate=True) def testBuildArgumentString_031(self): """Test with valid source and target, validate=True.""" options = Options() options.sourceDir = "source" options.s3BucketUrl = "bucket" argumentString = options.buildArgumentString(validate=True) self.assertEqual('"source" "bucket" ', argumentString) def testBuildArgumentString_032(self): """Test with all values set (except managed ones), actions containing one item, validate=True.""" options = Options() options.help = True options.version = True options.verbose = True options.quiet = True options.logfile = "logfile" options.owner = ("a", "b") options.mode = "631" options.output = True options.debug = True options.stacktrace = True options.diagnostics = True options.verifyOnly = True options.ignoreWarnings = True options.sourceDir = "source" options.s3BucketUrl = "bucket" argumentString = options.buildArgumentString(validate=True) self.assertEqual( '--help --version --verbose --quiet --logfile "logfile" --owner "a:b" --mode 631 --output --debug --stack --diagnostics --verifyOnly --ignoreWarnings "source" "bucket" ', argumentString, ) def testBuildArgumentString_033(self): """Test with uploadOnly set, validate=False.""" options = Options() options.uploadOnly = True argumentString = options.buildArgumentString(validate=False) self.assertEqual("--uploadOnly ", argumentString) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1649563 cedar_backup3-3.8.1/tests/test_util.py0000644000000000000000000044465414567004737014725 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2008,2010,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests utility functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # pylint: disable=C0322,C0324 ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/util.py. Code Coverage ============= This module contains individual tests for the public functions and classes implemented in util.py. Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== All of the tests in this module are considered safe to be run in an average build environment. There is a no need to use a UTILTESTS_FULL environment variable to provide a "reduced feature set" test suite as for some of the other test modules. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import logging import os import sys import tempfile import time import unittest from os.path import isdir from CedarBackup3.testutil import ( buildPath, captureOutput, configureLogging, extractTar, findResources, platformSupportsLinks, platformWindows, removedir, ) from CedarBackup3.util import ( UNIT_BYTES, UNIT_GBYTES, UNIT_KBYTES, UNIT_MBYTES, UNIT_SECTORS, AbsolutePathList, Diagnostics, DirectedGraph, ObjectTypeList, PathResolverSingleton, RegexList, RegexMatchList, RestrictedContentList, UnorderedList, buildNormalizedPath, convertSize, dereferenceLink, deriveDayOfWeek, displayBytes, encodePath, executeCommand, getFunctionReference, isStartOfWeek, nullDevice, parseCommaSeparatedString, pathJoin, resolveCommand, sortDict, splitCommandLine, ) ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = ["./data", "./tests/data"] RESOURCES = [ "lotsoflines.py", "tree10.tar.gz", ] # This is a command that is always valid on the platform, something other than the Python interpreter # The command must return a success status (zero) for the tests to pass if platformWindows(): VALID_COMMAND = [pathJoin(os.environ["SystemRoot"], "system32", "WindowsPowerShell", "v1.0", "powershell.exe")] VALID_ARGS = ["Write-Output hello"] VALID_OUTPUT = "hello\r\n" else: VALID_COMMAND = ["echo"] VALID_ARGS = ["hello"] VALID_OUTPUT = "hello\n" ####################################################################### # Test Case Classes ####################################################################### ########################## # TestUnorderedList class ########################## class TestUnorderedList(unittest.TestCase): """Tests for the UnorderedList class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ################################## # Test unordered list comparisons ################################## def testComparison_001(self): """ Test two empty lists. """ list1 = UnorderedList() list2 = UnorderedList() self.assertEqual(list1, list2) self.assertEqual(list2, list1) def testComparison_002(self): """ Test empty vs. non-empty list. """ list1 = UnorderedList() list2 = UnorderedList() list1.append(1) list1.append(2) list1.append(3) list1.append(4) self.assertEqual([1, 2, 3, 4], list1) self.assertEqual([2, 3, 4, 1], list1) self.assertEqual([3, 4, 1, 2], list1) self.assertEqual([4, 1, 2, 3], list1) self.assertEqual(list1, [4, 3, 2, 1]) self.assertEqual(list1, [3, 2, 1, 4]) self.assertEqual(list1, [2, 1, 4, 3]) self.assertEqual(list1, [1, 4, 3, 2]) self.assertNotEqual(list1, list2) self.assertNotEqual(list2, list1) def testComparison_003(self): """ Test two non-empty lists, completely different contents. """ list1 = UnorderedList() list2 = UnorderedList() list1.append(1) list1.append(2) list1.append(3) list1.append(4) list2.append("a") list2.append("b") list2.append("c") list2.append("d") self.assertEqual([1, 2, 3, 4], list1) self.assertEqual([2, 3, 4, 1], list1) self.assertEqual([3, 4, 1, 2], list1) self.assertEqual([4, 1, 2, 3], list1) self.assertEqual(list1, [4, 3, 2, 1]) self.assertEqual(list1, [3, 2, 1, 4]) self.assertEqual(list1, [2, 1, 4, 3]) self.assertEqual(list1, [1, 4, 3, 2]) self.assertEqual(["a", "b", "c", "d"], list2) self.assertEqual(["b", "c", "d", "a"], list2) self.assertEqual(["c", "d", "a", "b"], list2) self.assertEqual(["d", "a", "b", "c"], list2) self.assertEqual(list2, ["d", "c", "b", "a"]) self.assertEqual(list2, ["c", "b", "a", "d"]) self.assertEqual(list2, ["b", "a", "d", "c"]) self.assertEqual(list2, ["a", "d", "c", "b"]) self.assertNotEqual(list1, list2) self.assertNotEqual(list2, list1) def testComparison_004(self): """ Test two non-empty lists, different but overlapping contents. """ list1 = UnorderedList() list2 = UnorderedList() list1.append(1) list1.append(2) list1.append(3) list1.append(4) list2.append(3) list2.append(4) list2.append("a") list2.append("b") self.assertEqual([1, 2, 3, 4], list1) self.assertEqual([2, 3, 4, 1], list1) self.assertEqual([3, 4, 1, 2], list1) self.assertEqual([4, 1, 2, 3], list1) self.assertEqual(list1, [4, 3, 2, 1]) self.assertEqual(list1, [3, 2, 1, 4]) self.assertEqual(list1, [2, 1, 4, 3]) self.assertEqual(list1, [1, 4, 3, 2]) self.assertEqual([3, 4, "a", "b"], list2) self.assertEqual([4, "a", "b", 3], list2) self.assertEqual(["a", "b", 3, 4], list2) self.assertEqual(["b", 3, 4, "a"], list2) self.assertEqual(list2, ["b", "a", 4, 3]) self.assertEqual(list2, ["a", 4, 3, "b"]) self.assertEqual(list2, [4, 3, "b", "a"]) self.assertEqual(list2, [3, "b", "a", 4]) self.assertNotEqual(list1, list2) self.assertNotEqual(list2, list1) def testComparison_005(self): """ Test two non-empty lists, exactly the same contents, same order. """ list1 = UnorderedList() list2 = UnorderedList() list1.append(1) list1.append(2) list1.append(3) list1.append(4) list2.append(1) list2.append(2) list2.append(3) list2.append(4) self.assertEqual([1, 2, 3, 4], list1) self.assertEqual([2, 3, 4, 1], list1) self.assertEqual([3, 4, 1, 2], list1) self.assertEqual([4, 1, 2, 3], list1) self.assertEqual(list1, [4, 3, 2, 1]) self.assertEqual(list1, [3, 2, 1, 4]) self.assertEqual(list1, [2, 1, 4, 3]) self.assertEqual(list1, [1, 4, 3, 2]) self.assertEqual([1, 2, 3, 4], list2) self.assertEqual([2, 3, 4, 1], list2) self.assertEqual([3, 4, 1, 2], list2) self.assertEqual([4, 1, 2, 3], list2) self.assertEqual(list2, [4, 3, 2, 1]) self.assertEqual(list2, [3, 2, 1, 4]) self.assertEqual(list2, [2, 1, 4, 3]) self.assertEqual(list2, [1, 4, 3, 2]) self.assertEqual(list1, list2) self.assertEqual(list2, list1) def testComparison_006(self): """ Test two non-empty lists, exactly the same contents, different order. """ list1 = UnorderedList() list2 = UnorderedList() list1.append(1) list1.append(2) list1.append(3) list1.append(4) list2.append(3) list2.append(1) list2.append(2) list2.append(4) self.assertEqual([1, 2, 3, 4], list1) self.assertEqual([2, 3, 4, 1], list1) self.assertEqual([3, 4, 1, 2], list1) self.assertEqual([4, 1, 2, 3], list1) self.assertEqual(list1, [4, 3, 2, 1]) self.assertEqual(list1, [3, 2, 1, 4]) self.assertEqual(list1, [2, 1, 4, 3]) self.assertEqual(list1, [1, 4, 3, 2]) self.assertEqual([1, 2, 3, 4], list2) self.assertEqual([2, 3, 4, 1], list2) self.assertEqual([3, 4, 1, 2], list2) self.assertEqual([4, 1, 2, 3], list2) self.assertEqual(list2, [4, 3, 2, 1]) self.assertEqual(list2, [3, 2, 1, 4]) self.assertEqual(list2, [2, 1, 4, 3]) self.assertEqual(list2, [1, 4, 3, 2]) self.assertEqual(list1, list2) self.assertEqual(list2, list1) def testComparison_007(self): """ Test two non-empty lists, exactly the same contents, some duplicates, same order. """ list1 = UnorderedList() list2 = UnorderedList() list1.append(1) list1.append(2) list1.append(2) list1.append(3) list1.append(4) list1.append(4) list2.append(1) list2.append(2) list2.append(2) list2.append(3) list2.append(4) list2.append(4) self.assertEqual([1, 2, 2, 3, 4, 4], list1) self.assertEqual([2, 2, 3, 4, 1, 4], list1) self.assertEqual([2, 3, 4, 1, 4, 2], list1) self.assertEqual([2, 4, 1, 4, 2, 3], list1) self.assertEqual(list1, [1, 2, 2, 3, 4, 4]) self.assertEqual(list1, [2, 2, 3, 4, 1, 4]) self.assertEqual(list1, [2, 3, 4, 1, 4, 2]) self.assertEqual(list1, [2, 4, 1, 4, 2, 3]) self.assertEqual([1, 2, 2, 3, 4, 4], list2) self.assertEqual([2, 2, 3, 4, 1, 4], list2) self.assertEqual([2, 3, 4, 1, 4, 2], list2) self.assertEqual([2, 4, 1, 4, 2, 3], list2) self.assertEqual(list2, [1, 2, 2, 3, 4, 4]) self.assertEqual(list2, [2, 2, 3, 4, 1, 4]) self.assertEqual(list2, [2, 3, 4, 1, 4, 2]) self.assertEqual(list2, [2, 4, 1, 4, 2, 3]) self.assertEqual(list1, list2) self.assertEqual(list2, list1) def testComparison_008(self): """ Test two non-empty lists, exactly the same contents, some duplicates, different order. """ list1 = UnorderedList() list2 = UnorderedList() list1.append(1) list1.append(2) list1.append(2) list1.append(3) list1.append(4) list1.append(4) list2.append(3) list2.append(1) list2.append(2) list2.append(2) list2.append(4) list2.append(4) self.assertEqual([1, 2, 2, 3, 4, 4], list1) self.assertEqual([2, 2, 3, 4, 1, 4], list1) self.assertEqual([2, 3, 4, 1, 4, 2], list1) self.assertEqual([2, 4, 1, 4, 2, 3], list1) self.assertEqual(list1, [1, 2, 2, 3, 4, 4]) self.assertEqual(list1, [2, 2, 3, 4, 1, 4]) self.assertEqual(list1, [2, 3, 4, 1, 4, 2]) self.assertEqual(list1, [2, 4, 1, 4, 2, 3]) self.assertEqual([1, 2, 2, 3, 4, 4], list2) self.assertEqual([2, 2, 3, 4, 1, 4], list2) self.assertEqual([2, 3, 4, 1, 4, 2], list2) self.assertEqual([2, 4, 1, 4, 2, 3], list2) self.assertEqual(list2, [1, 2, 2, 3, 4, 4]) self.assertEqual(list2, [2, 2, 3, 4, 1, 4]) self.assertEqual(list2, [2, 3, 4, 1, 4, 2]) self.assertEqual(list2, [2, 4, 1, 4, 2, 3]) self.assertEqual(list1, list2) self.assertEqual(list2, list1) ############################# # TestAbsolutePathList class ############################# class TestAbsolutePathList(unittest.TestCase): """Tests for the AbsolutePathList class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ####################### # Test list operations ####################### def testListOperations_001(self): """ Test append() for a valid absolute path. """ list1 = AbsolutePathList() list1.append("/path/to/something/absolute") self.assertEqual(list1, ["/path/to/something/absolute"]) self.assertEqual(list1[0], "/path/to/something/absolute") list1.append("/path/to/something/else") self.assertEqual(list1, ["/path/to/something/absolute", "/path/to/something/else"]) self.assertEqual(list1[0], "/path/to/something/absolute") self.assertEqual(list1[1], "/path/to/something/else") def testListOperations_002(self): """ Test append() for an invalid, non-absolute path. """ list1 = AbsolutePathList() self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "path/to/something/relative") self.assertEqual(list1, []) def testListOperations_003(self): """ Test insert() for a valid absolute path. """ list1 = AbsolutePathList() list1.insert(0, "/path/to/something/absolute") self.assertEqual(list1, ["/path/to/something/absolute"]) self.assertEqual(list1[0], "/path/to/something/absolute") list1.insert(0, "/path/to/something/else") self.assertEqual(list1, ["/path/to/something/else", "/path/to/something/absolute"]) self.assertEqual(list1[0], "/path/to/something/else") self.assertEqual(list1[1], "/path/to/something/absolute") def testListOperations_004(self): """ Test insert() for an invalid, non-absolute path. """ list1 = AbsolutePathList() self.assertRaises(ValueError, list1.insert, 0, "path/to/something/relative") def testListOperations_005(self): """ Test extend() for a valid absolute path. """ list1 = AbsolutePathList() list1.extend(["/path/to/something/absolute"]) self.assertEqual(list1, ["/path/to/something/absolute"]) self.assertEqual(list1[0], "/path/to/something/absolute") list1.extend(["/path/to/something/else"]) self.assertEqual(list1, ["/path/to/something/absolute", "/path/to/something/else"]) self.assertEqual(list1[0], "/path/to/something/absolute") self.assertEqual(list1[1], "/path/to/something/else") def testListOperations_006(self): """ Test extend() for an invalid, non-absolute path. """ list1 = AbsolutePathList() self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["path/to/something/relative"]) self.assertEqual(list1, []) ########################### # TestObjectTypeList class ########################### class TestObjectTypeList(unittest.TestCase): """Tests for the ObjectTypeList class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): pass def tearDown(self): pass ####################### # Test list operations ####################### def testListOperations_001(self): """ Test append() for a valid object type. """ list1 = ObjectTypeList(str, "str") list1.append("string") self.assertEqual(list1, ["string"]) self.assertEqual(list1[0], "string") list1.append("string2") self.assertEqual(list1, ["string", "string2"]) self.assertEqual(list1[0], "string") self.assertEqual(list1[1], "string2") def testListOperations_002(self): """ Test append() for an invalid object type. """ list1 = ObjectTypeList(str, "str") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, 1) self.assertEqual(list1, []) def testListOperations_003(self): """ Test insert() for a valid object type. """ list1 = ObjectTypeList(str, "str") list1.insert(0, "string") self.assertEqual(list1, ["string"]) self.assertEqual(list1[0], "string") list1.insert(0, "string2") self.assertEqual(list1, ["string2", "string"]) self.assertEqual(list1[0], "string2") self.assertEqual(list1[1], "string") def testListOperations_004(self): """ Test insert() for an invalid object type. """ list1 = ObjectTypeList(str, "str") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, AbsolutePathList()) self.assertEqual(list1, []) def testListOperations_005(self): """ Test extend() for a valid object type. """ list1 = ObjectTypeList(str, "str") list1.extend(["string"]) self.assertEqual(list1, ["string"]) self.assertEqual(list1[0], "string") list1.extend(["string2"]) self.assertEqual(list1, ["string", "string2"]) self.assertEqual(list1[0], "string") self.assertEqual(list1[1], "string2") def testListOperations_006(self): """ Test extend() for an invalid object type. """ list1 = ObjectTypeList(str, "str") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, [12.0]) self.assertEqual(list1, []) ################################## # TestRestrictedContentList class ################################## class TestRestrictedContentList(unittest.TestCase): """Tests for the RestrictedContentList class.""" ################ # Setup methods ################ def setUp(self): pass def tearDown(self): pass ####################### # Test list operations ####################### def testListOperations_001(self): """ Test append() for a valid value. """ list1 = RestrictedContentList(["a", "b", "c"], "values") list1.append("a") self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.append("b") self.assertEqual(list1, ["a", "b"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "b") list1.append("c") self.assertEqual(list1, ["a", "b", "c"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "b") self.assertEqual(list1[2], "c") def testListOperations_002(self): """ Test append() for an invalid value. """ list1 = RestrictedContentList(["a", "b", "c"], "values") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "d") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, 1) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, UnorderedList()) self.assertEqual(list1, []) def testListOperations_003(self): """ Test insert() for a valid value. """ list1 = RestrictedContentList(["a", "b", "c"], "values") list1.insert(0, "a") self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.insert(0, "b") self.assertEqual(list1, ["b", "a"]) self.assertEqual(list1[0], "b") self.assertEqual(list1[1], "a") list1.insert(0, "c") self.assertEqual(list1, ["c", "b", "a"]) self.assertEqual(list1[0], "c") self.assertEqual(list1[1], "b") self.assertEqual(list1[2], "a") def testListOperations_004(self): """ Test insert() for an invalid value. """ list1 = RestrictedContentList(["a", "b", "c"], "values") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, "d") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, 1) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, UnorderedList()) self.assertEqual(list1, []) def testListOperations_005(self): """ Test extend() for a valid value. """ list1 = RestrictedContentList(["a", "b", "c"], "values") list1.extend(["a"]) self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.extend(["b"]) self.assertEqual(list1, ["a", "b"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "b") list1.extend(["c"]) self.assertEqual(list1, ["a", "b", "c"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "b") self.assertEqual(list1[2], "c") def testListOperations_006(self): """ Test extend() for an invalid value. """ list1 = RestrictedContentList(["a", "b", "c"], "values") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["d"]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, [1]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, [UnorderedList()]) self.assertEqual(list1, []) ########################### # TestRegexMatchList class ########################### class TestRegexMatchList(unittest.TestCase): """Tests for the RegexMatchList class.""" ################ # Setup methods ################ def setUp(self): pass def tearDown(self): pass ####################### # Test list operations ####################### def testListOperations_001(self): """ Test append() for a valid value, emptyAllowed=True. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=True) list1.append("a") self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.append("1") self.assertEqual(list1, ["a", "1"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") list1.append("abcd12345") self.assertEqual(list1, ["a", "1", "abcd12345"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") self.assertEqual(list1[2], "abcd12345") list1.append("") self.assertEqual(list1, ["a", "1", "abcd12345", ""]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") self.assertEqual(list1[2], "abcd12345") self.assertEqual(list1[3], "") def testListOperations_002(self): """ Test append() for an invalid value, emptyAllowed=True. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=True) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "A") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "ABC") self.assertEqual(list1, []) self.assertRaises(TypeError, list1.append, 12) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "KEN_12") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, None) self.assertEqual(list1, []) def testListOperations_003(self): """ Test insert() for a valid value, emptyAllowed=True. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=True) list1.insert(0, "a") self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.insert(0, "1") self.assertEqual(list1, ["1", "a"]) self.assertEqual(list1[0], "1") self.assertEqual(list1[1], "a") list1.insert(0, "abcd12345") self.assertEqual(list1, ["abcd12345", "1", "a"]) self.assertEqual(list1[0], "abcd12345") self.assertEqual(list1[1], "1") self.assertEqual(list1[2], "a") list1.insert(0, "") self.assertEqual(list1, ["abcd12345", "1", "a", ""]) self.assertEqual(list1[0], "") self.assertEqual(list1[1], "abcd12345") self.assertEqual(list1[2], "1") self.assertEqual(list1[3], "a") def testListOperations_004(self): """ Test insert() for an invalid value, emptyAllowed=True. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=True) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, "A") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, "ABC") self.assertEqual(list1, []) self.assertRaises(TypeError, list1.insert, 0, 12) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, "KEN_12") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, None) self.assertEqual(list1, []) def testListOperations_005(self): """ Test extend() for a valid value, emptyAllowed=True. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=True) list1.extend(["a"]) self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.extend(["1"]) self.assertEqual(list1, ["a", "1"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") list1.extend(["abcd12345"]) self.assertEqual(list1, ["a", "1", "abcd12345"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") self.assertEqual(list1[2], "abcd12345") list1.extend([""]) self.assertEqual(list1, ["a", "1", "abcd12345", ""]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") self.assertEqual(list1[2], "abcd12345") self.assertEqual(list1[3], "") def testListOperations_006(self): """ Test extend() for an invalid value, emptyAllowed=True. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=True) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["A"]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["ABC"]) self.assertEqual(list1, []) self.assertRaises(TypeError, list1.extend, [12]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["KEN_12"]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, [None]) self.assertEqual(list1, []) def testListOperations_007(self): """ Test append() for a valid value, emptyAllowed=False. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=False) list1.append("a") self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.append("1") self.assertEqual(list1, ["a", "1"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") list1.append("abcd12345") self.assertEqual(list1, ["a", "1", "abcd12345"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") self.assertEqual(list1[2], "abcd12345") def testListOperations_008(self): """ Test append() for an invalid value, emptyAllowed=False. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=False) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "A") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "ABC") self.assertEqual(list1, []) self.assertRaises(TypeError, list1.append, 12) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "KEN_12") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, None) self.assertEqual(list1, []) def testListOperations_009(self): """ Test insert() for a valid value, emptyAllowed=False. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=False) list1.insert(0, "a") self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.insert(0, "1") self.assertEqual(list1, ["1", "a"]) self.assertEqual(list1[0], "1") self.assertEqual(list1[1], "a") list1.insert(0, "abcd12345") self.assertEqual(list1, ["abcd12345", "1", "a"]) self.assertEqual(list1[0], "abcd12345") self.assertEqual(list1[1], "1") self.assertEqual(list1[2], "a") def testListOperations_010(self): """ Test insert() for an invalid value, emptyAllowed=False. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=False) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, "A") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, "ABC") self.assertEqual(list1, []) self.assertRaises(TypeError, list1.insert, 0, 12) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, "KEN_12") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, "") self.assertEqual(list1, []) self.assertRaises(ValueError, list1.insert, 0, None) self.assertEqual(list1, []) def testListOperations_011(self): """ Test extend() for a valid value, emptyAllowed=False. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=False) list1.extend(["a"]) self.assertEqual(list1, ["a"]) self.assertEqual(list1[0], "a") list1.extend(["1"]) self.assertEqual(list1, ["a", "1"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") list1.extend(["abcd12345"]) self.assertEqual(list1, ["a", "1", "abcd12345"]) self.assertEqual(list1[0], "a") self.assertEqual(list1[1], "1") self.assertEqual(list1[2], "abcd12345") def testListOperations_012(self): """ Test extend() for an invalid value, emptyAllowed=False. """ list1 = RegexMatchList(r"^[a-z0-9]*$", emptyAllowed=False) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["A"]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["ABC"]) self.assertEqual(list1, []) self.assertRaises(TypeError, list1.extend, [12]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["KEN_12"]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, [""]) self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, [None]) self.assertEqual(list1, []) ###################### # TestRegexList class ###################### class TestRegexList(unittest.TestCase): """Tests for the RegexList class.""" ################ # Setup methods ################ def setUp(self): pass def tearDown(self): pass ####################### # Test list operations ####################### def testListOperations_001(self): """ Test append() for a valid regular expresson. """ list1 = RegexList() list1.append(r".*\.jpg") self.assertEqual(list1, [r".*\.jpg"]) self.assertEqual(list1[0], r".*\.jpg") list1.append("[a-zA-Z0-9]*") self.assertEqual(list1, [r".*\.jpg", "[a-zA-Z0-9]*"]) self.assertEqual(list1[0], r".*\.jpg") self.assertEqual(list1[1], "[a-zA-Z0-9]*") def testListOperations_002(self): """ Test append() for an invalid regular expression. """ list1 = RegexList() self.assertEqual(list1, []) self.assertRaises(ValueError, list1.append, "*.jpg") self.assertEqual(list1, []) def testListOperations_003(self): """ Test insert() for a valid regular expression. """ list1 = RegexList() list1.insert(0, r".*\.jpg") self.assertEqual(list1, [r".*\.jpg"]) self.assertEqual(list1[0], r".*\.jpg") list1.insert(0, "[a-zA-Z0-9]*") self.assertEqual(list1, ["[a-zA-Z0-9]*", r".*\.jpg"]) self.assertEqual(list1[0], "[a-zA-Z0-9]*") self.assertEqual(list1[1], r".*\.jpg") def testListOperations_004(self): """ Test insert() for an invalid regular expression. """ list1 = RegexList() self.assertRaises(ValueError, list1.insert, 0, "*.jpg") def testListOperations_005(self): """ Test extend() for a valid regular expression. """ list1 = RegexList() list1.extend([r".*\.jpg"]) self.assertEqual(list1, [r".*\.jpg"]) self.assertEqual(list1[0], r".*\.jpg") list1.extend(["[a-zA-Z0-9]*"]) self.assertEqual(list1, [r".*\.jpg", "[a-zA-Z0-9]*"]) self.assertEqual(list1[0], r".*\.jpg") self.assertEqual(list1[1], "[a-zA-Z0-9]*") def testListOperations_006(self): """ Test extend() for an invalid regular expression. """ list1 = RegexList() self.assertEqual(list1, []) self.assertRaises(ValueError, list1.extend, ["*.jpg"]) self.assertEqual(list1, []) ########################## # TestDirectedGraph class ########################## class TestDirectedGraph(unittest.TestCase): """Tests for the DirectedGraph class.""" ############################ # Test __repr__ and __str__ ############################ def testStringFuncs_001(self): """ Just make sure that the string functions don't have errors (i.e. bad variable names). """ obj = DirectedGraph("test") obj.__repr__() obj.__str__() ################################## # Test constructor and attributes ################################## def testConstructor_001(self): """ Test constructor with a valid name filled in. """ graph = DirectedGraph("Ken") self.assertEqual("Ken", graph.name) def testConstructor_002(self): """ Test constructor with a ``None`` name filled in. """ self.assertRaises(ValueError, DirectedGraph, None) ########################## # Test depth first search ########################## def testTopologicalSort_001(self): """ Empty graph. """ graph = DirectedGraph("test") path = graph.topologicalSort() self.assertEqual([], path) def testTopologicalSort_002(self): """ Graph with 1 vertex, no edges. """ graph = DirectedGraph("test") graph.createVertex("1") path = graph.topologicalSort() self.assertEqual(["1"], path) def testTopologicalSort_003(self): """ Graph with 2 vertices, no edges. """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") path = graph.topologicalSort() self.assertEqual(["2", "1"], path) def testTopologicalSort_004(self): """ Graph with 3 vertices, no edges. """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_005(self): """ Graph with 4 vertices, no edges. """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createVertex("4") path = graph.topologicalSort() self.assertEqual(["4", "2", "1", "3"], path) def testTopologicalSort_006(self): """ Graph with 4 vertices, no edges. """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createVertex("4") graph.createVertex("5") path = graph.topologicalSort() self.assertEqual(["5", "4", "2", "1", "3"], path) def testTopologicalSort_007(self): """ Graph with 3 vertices, in a chain (1->2->3), create order (1,2,3) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("1", "2") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_008(self): """ Graph with 3 vertices, in a chain (1->2->3), create order (1,3,2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("3") graph.createVertex("2") graph.createEdge("1", "2") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_009(self): """ Graph with 3 vertices, in a chain (1->2->3), create order (2,3,1) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("3") graph.createVertex("1") graph.createEdge("1", "2") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_010(self): """ Graph with 3 vertices, in a chain (1->2->3), create order (2,1,3) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("1") graph.createVertex("3") graph.createEdge("1", "2") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_011(self): """ Graph with 3 vertices, in a chain (1->2->3), create order (3,1,2) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createEdge("1", "2") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_012(self): """ Graph with 3 vertices, in a chain (1->2->3), create order (3,2,1) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("2") graph.createVertex("1") graph.createEdge("1", "2") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_013(self): """ Graph with 3 vertices, in a chain (3->2->1), create order (1,2,3) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("3", "2") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_014(self): """ Graph with 3 vertices, in a chain (3->2->1), create order (1,3,2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("3") graph.createVertex("2") graph.createEdge("3", "2") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_015(self): """ Graph with 3 vertices, in a chain (3->2->1), create order (2,3,1) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("3") graph.createVertex("1") graph.createEdge("3", "2") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_016(self): """ Graph with 3 vertices, in a chain (3->2->1), create order (2,1,3) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("1") graph.createVertex("3") graph.createEdge("3", "2") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_017(self): """ Graph with 3 vertices, in a chain (3->2->1), create order (3,1,2) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createEdge("3", "2") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_018(self): """ Graph with 3 vertices, in a chain (3->2->1), create order (3,2,1) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("2") graph.createVertex("1") graph.createEdge("3", "2") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_019(self): """ Graph with 3 vertices, chain and orphan (1->2,3), create order (1,2,3) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("1", "2") path = graph.topologicalSort() self.assertEqual(["3", "1", "2"], path) def testTopologicalSort_020(self): """ Graph with 3 vertices, chain and orphan (1->2,3), create order (1,3,2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("3") graph.createVertex("2") graph.createEdge("1", "2") path = graph.topologicalSort() self.assertEqual(["3", "1", "2"], path) def testTopologicalSort_021(self): """ Graph with 3 vertices, chain and orphan (1->2,3), create order (2,3,1) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("3") graph.createVertex("1") graph.createEdge("1", "2") path = graph.topologicalSort() self.assertEqual(["1", "3", "2"], path) def testTopologicalSort_022(self): """ Graph with 3 vertices, chain and orphan (1->2,3), create order (2,1,3) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("1") graph.createVertex("3") graph.createEdge("1", "2") path = graph.topologicalSort() self.assertEqual(["3", "1", "2"], path) def testTopologicalSort_023(self): """ Graph with 3 vertices, chain and orphan (1->2,3), create order (3,1,2) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createEdge("1", "2") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_024(self): """ Graph with 3 vertices, chain and orphan (1->2,3), create order (3,2,1) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("2") graph.createVertex("1") graph.createEdge("1", "2") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_025(self): """ Graph with 3 vertices, chain and orphan (1->3,2), create order (1,2,3) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("1", "3") path = graph.topologicalSort() self.assertEqual(["2", "1", "3"], path) def testTopologicalSort_026(self): """ Graph with 3 vertices, chain and orphan (1->3,2), create order (1,3,2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("3") graph.createVertex("2") graph.createEdge("1", "3") path = graph.topologicalSort() self.assertEqual(["2", "1", "3"], path) def testTopologicalSort_027(self): """ Graph with 3 vertices, chain and orphan (1->3,2), create order (2,3,1) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("3") graph.createVertex("1") graph.createEdge("1", "3") path = graph.topologicalSort() self.assertEqual(["1", "3", "2"], path) def testTopologicalSort_028(self): """ Graph with 3 vertices, chain and orphan (1->3,2), create order (2,1,3) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("1") graph.createVertex("3") graph.createEdge("1", "3") path = graph.topologicalSort() self.assertEqual(["1", "3", "2"], path) def testTopologicalSort_029(self): """ Graph with 3 vertices, chain and orphan (1->3,2), create order (3,1,2) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createEdge("1", "3") path = graph.topologicalSort() self.assertEqual(["2", "1", "3"], path) def testTopologicalSort_030(self): """ Graph with 3 vertices, chain and orphan (1->3,2), create order (3,2,1) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("2") graph.createVertex("1") graph.createEdge("1", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_031(self): """ Graph with 3 vertices, chain and orphan (2->3,1), create order (1,2,3) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["2", "3", "1"], path) def testTopologicalSort_032(self): """ Graph with 3 vertices, chain and orphan (2->3,1), create order (1,3,2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("3") graph.createVertex("2") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["2", "3", "1"], path) def testTopologicalSort_033(self): """ Graph with 3 vertices, chain and orphan (2->3,1), create order (2,3,1) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("3") graph.createVertex("1") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_034(self): """ Graph with 3 vertices, chain and orphan (2->3,1), create order (2,1,3) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("1") graph.createVertex("3") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_035(self): """ Graph with 3 vertices, chain and orphan (2->3,1), create order (3,1,2) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["2", "1", "3"], path) def testTopologicalSort_036(self): """ Graph with 3 vertices, chain and orphan (2->3,1), create order (3,2,1) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("2") graph.createVertex("1") graph.createEdge("2", "3") path = graph.topologicalSort() self.assertEqual(["1", "2", "3"], path) def testTopologicalSort_037(self): """ Graph with 3 vertices, chain and orphan (2->1,3), create order (1,2,3) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_038(self): """ Graph with 3 vertices, chain and orphan (2->1,3), create order (1,3,2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("3") graph.createVertex("2") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["2", "3", "1"], path) def testTopologicalSort_039(self): """ Graph with 3 vertices, chain and orphan (2->1,3), create order (2,3,1) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("3") graph.createVertex("1") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_040(self): """ Graph with 3 vertices, chain and orphan (2->1,3), create order (2,1,3) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("1") graph.createVertex("3") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_041(self): """ Graph with 3 vertices, chain and orphan (2->1,3), create order (3,1,2) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["2", "1", "3"], path) def testTopologicalSort_042(self): """ Graph with 3 vertices, chain and orphan (2->1,3), create order (3,2,1) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("2") graph.createVertex("1") graph.createEdge("2", "1") path = graph.topologicalSort() self.assertEqual(["2", "1", "3"], path) def testTopologicalSort_043(self): """ Graph with 3 vertices, chain and orphan (3->1,2), create order (1,2,3) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("3", "1") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_044(self): """ Graph with 3 vertices, chain and orphan (3->1,2), create order (1,3,2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("3") graph.createVertex("2") graph.createEdge("3", "1") path = graph.topologicalSort() self.assertEqual(["2", "3", "1"], path) def testTopologicalSort_045(self): """ Graph with 3 vertices, chain and orphan (3->1,2), create order (2,3,1) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("3") graph.createVertex("1") graph.createEdge("3", "1") path = graph.topologicalSort() self.assertEqual(["3", "1", "2"], path) def testTopologicalSort_046(self): """ Graph with 3 vertices, chain and orphan (3->1,2), create order (2,1,3) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("1") graph.createVertex("3") graph.createEdge("3", "1") path = graph.topologicalSort() self.assertEqual(["3", "1", "2"], path) def testTopologicalSort_047(self): """ Graph with 3 vertices, chain and orphan (3->1,2), create order (3,1,2) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createEdge("3", "1") path = graph.topologicalSort() self.assertEqual(["2", "3", "1"], path) def testTopologicalSort_048(self): """ Graph with 3 vertices, chain and orphan (3->1,2), create order (3,2,1) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("2") graph.createVertex("1") graph.createEdge("3", "1") path = graph.topologicalSort() self.assertEqual(["2", "3", "1"], path) def testTopologicalSort_049(self): """ Graph with 3 vertices, chain and orphan (3->2,1), create order (1,2,3) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("3", "2") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_050(self): """ Graph with 3 vertices, chain and orphan (3->2,1), create order (1,3,2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("3") graph.createVertex("2") graph.createEdge("3", "2") path = graph.topologicalSort() self.assertEqual(["3", "2", "1"], path) def testTopologicalSort_051(self): """ Graph with 3 vertices, chain and orphan (3->2,1), create order (2,3,1) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("3") graph.createVertex("1") graph.createEdge("3", "2") path = graph.topologicalSort() self.assertEqual(["1", "3", "2"], path) def testTopologicalSort_052(self): """ Graph with 3 vertices, chain and orphan (3->2,1), create order (2,1,3) """ graph = DirectedGraph("test") graph.createVertex("2") graph.createVertex("1") graph.createVertex("3") graph.createEdge("3", "2") path = graph.topologicalSort() self.assertEqual(["3", "1", "2"], path) def testTopologicalSort_053(self): """ Graph with 3 vertices, chain and orphan (3->2,1), create order (3,1,2) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("1") graph.createVertex("2") graph.createEdge("3", "2") path = graph.topologicalSort() self.assertEqual(["1", "3", "2"], path) def testTopologicalSort_054(self): """ Graph with 3 vertices, chain and orphan (3->2,1), create order (3,2,1) """ graph = DirectedGraph("test") graph.createVertex("3") graph.createVertex("2") graph.createVertex("1") graph.createEdge("3", "2") path = graph.topologicalSort() self.assertEqual(["1", "3", "2"], path) def testTopologicalSort_055(self): """ Graph with 1 vertex, with an edge to itself (1->1). """ graph = DirectedGraph("test") graph.createVertex("1") graph.createEdge("1", "1") self.assertRaises(ValueError, graph.topologicalSort) def testTopologicalSort_056(self): """ Graph with 2 vertices, each with an edge to itself (1->1, 2->2). """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createEdge("1", "1") graph.createEdge("2", "2") self.assertRaises(ValueError, graph.topologicalSort) def testTopologicalSort_057(self): """ Graph with 3 vertices, each with an edge to itself (1->1, 2->2, 3->3). """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("1", "1") graph.createEdge("2", "2") graph.createEdge("3", "3") self.assertRaises(ValueError, graph.topologicalSort) def testTopologicalSort_058(self): """ Graph with 3 vertices, in a loop (1->2->3->1). """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createEdge("1", "2") graph.createEdge("2", "3") graph.createEdge("3", "1") self.assertRaises(ValueError, graph.topologicalSort) def testTopologicalSort_059(self): """ Graph with 5 vertices, (2, 1->3, 1->4, 1->5) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createVertex("4") graph.createVertex("5") graph.createEdge("1", "3") graph.createEdge("1", "4") graph.createEdge("1", "5") path = graph.topologicalSort() self.assertEqual(["2", "1", "5", "4", "3"], path) def testTopologicalSort_060(self): """ Graph with 5 vertices, (1->3, 1->4, 1->5, 2->5) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createVertex("4") graph.createVertex("5") graph.createEdge("1", "3") graph.createEdge("1", "4") graph.createEdge("1", "5") graph.createEdge("2", "5") path = graph.topologicalSort() self.assertEqual(["2", "1", "5", "4", "3"], path) def testTopologicalSort_061(self): """ Graph with 5 vertices, (1->3, 1->4, 1->5, 2->5, 3->4) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createVertex("4") graph.createVertex("5") graph.createEdge("1", "3") graph.createEdge("1", "4") graph.createEdge("1", "5") graph.createEdge("2", "5") graph.createEdge("3", "4") path = graph.topologicalSort() self.assertEqual(["2", "1", "5", "3", "4"], path) def testTopologicalSort_062(self): """ Graph with 5 vertices, (1->3, 1->4, 1->5, 2->5, 3->4, 5->4) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createVertex("4") graph.createVertex("5") graph.createEdge("1", "3") graph.createEdge("1", "4") graph.createEdge("1", "5") graph.createEdge("2", "5") graph.createEdge("3", "4") graph.createEdge("5", "4") path = graph.topologicalSort() self.assertEqual(["2", "1", "5", "3", "4"], path) def testTopologicalSort_063(self): """ Graph with 5 vertices, (1->3, 1->4, 1->5, 2->5, 3->4, 5->4, 1->2) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createVertex("4") graph.createVertex("5") graph.createEdge("1", "3") graph.createEdge("1", "4") graph.createEdge("1", "5") graph.createEdge("2", "5") graph.createEdge("3", "4") graph.createEdge("5", "4") graph.createEdge("1", "2") path = graph.topologicalSort() self.assertEqual(["1", "2", "5", "3", "4"], path) def testTopologicalSort_064(self): """ Graph with 5 vertices, (1->3, 1->4, 1->5, 2->5, 3->4, 5->4, 1->2, 3->5) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createVertex("4") graph.createVertex("5") graph.createEdge("1", "3") graph.createEdge("1", "4") graph.createEdge("1", "5") graph.createEdge("2", "5") graph.createEdge("3", "4") graph.createEdge("5", "4") graph.createEdge("1", "2") graph.createEdge("3", "5") path = graph.topologicalSort() self.assertEqual(["1", "2", "3", "5", "4"], path) def testTopologicalSort_065(self): """ Graph with 5 vertices, (1->3, 1->4, 1->5, 2->5, 3->4, 5->4, 5->1) """ graph = DirectedGraph("test") graph.createVertex("1") graph.createVertex("2") graph.createVertex("3") graph.createVertex("4") graph.createVertex("5") graph.createEdge("1", "3") graph.createEdge("1", "4") graph.createEdge("1", "5") graph.createEdge("2", "5") graph.createEdge("3", "4") graph.createEdge("5", "4") graph.createEdge("5", "1") self.assertRaises(ValueError, graph.topologicalSort) ################################## # TestPathResolverSingleton class ################################## class TestPathResolverSingleton(unittest.TestCase): """Tests for the PathResolverSingleton class.""" ################ # Setup methods ################ def setUp(self): pass def tearDown(self): pass ########################## # Test singleton behavior ########################## def testBehavior_001(self): """ Check behavior of constructor around filling and clearing instance variable. """ PathResolverSingleton._instance = None instance = PathResolverSingleton() self.assertNotEqual(None, PathResolverSingleton._instance) self.assertTrue(instance is PathResolverSingleton._instance) def testBehavior_002(self): """ Check behavior of getInstance() around filling and clearing instance variable. """ PathResolverSingleton._instance = None instance1 = PathResolverSingleton.getInstance() instance2 = PathResolverSingleton.getInstance() instance3 = PathResolverSingleton.getInstance() self.assertNotEqual(None, PathResolverSingleton._instance) self.assertTrue(instance1 is PathResolverSingleton._instance) self.assertTrue(instance1 is instance2) self.assertTrue(instance1 is instance3) PathResolverSingleton._instance = None PathResolverSingleton() instance4 = PathResolverSingleton.getInstance() instance5 = PathResolverSingleton.getInstance() instance6 = PathResolverSingleton.getInstance() self.assertTrue(instance1 is not instance4) self.assertTrue(instance4 is PathResolverSingleton._instance) self.assertTrue(instance4 is instance5) self.assertTrue(instance4 is instance6) PathResolverSingleton._instance = None instance7 = PathResolverSingleton.getInstance() instance8 = PathResolverSingleton.getInstance() instance9 = PathResolverSingleton.getInstance() self.assertTrue(instance1 is not instance7) self.assertTrue(instance4 is not instance7) self.assertTrue(instance7 is PathResolverSingleton._instance) self.assertTrue(instance7 is instance8) self.assertTrue(instance7 is instance9) ############################ # Test lookup functionality ############################ def testLookup_001(self): """ Test that lookup() always returns default when singleton is empty. """ PathResolverSingleton._instance = None instance = PathResolverSingleton.getInstance() result = instance.lookup("whatever") self.assertEqual(result, None) result = instance.lookup("whatever", None) self.assertEqual(result, None) result = instance.lookup("other") self.assertEqual(result, None) result = instance.lookup("other", "default") self.assertEqual(result, "default") def testLookup_002(self): """ Test that lookup() returns proper values when singleton is not empty. """ mappings = {"one": "/path/to/one", "two": "/path/to/two"} PathResolverSingleton._instance = None singleton = PathResolverSingleton() singleton.fill(mappings) instance = PathResolverSingleton.getInstance() result = instance.lookup("whatever") self.assertEqual(result, None) result = instance.lookup("whatever", None) self.assertEqual(result, None) result = instance.lookup("other") self.assertEqual(result, None) result = instance.lookup("other", "default") self.assertEqual(result, "default") result = instance.lookup("one") self.assertEqual(result, "/path/to/one") result = instance.lookup("one", None) self.assertEqual(result, "/path/to/one") result = instance.lookup("two", None) self.assertEqual(result, "/path/to/two") result = instance.lookup("two", "default") self.assertEqual(result, "/path/to/two") ######################## # TestDiagnostics class ######################## class TestDiagnostics(unittest.TestCase): """Tests for the Diagnostics class.""" def testMethods_001(self): """ Test the version attribute. """ diagnostics = Diagnostics() self.assertFalse(diagnostics.version is None) self.assertNotEqual("", diagnostics.version) def testMethods_002(self): """ Test the interpreter attribute. """ diagnostics = Diagnostics() self.assertFalse(diagnostics.interpreter is None) self.assertNotEqual("", diagnostics.interpreter) def testMethods_003(self): """ Test the platform attribute. """ diagnostics = Diagnostics() self.assertFalse(diagnostics.platform is None) self.assertNotEqual("", diagnostics.platform) def testMethods_004(self): """ Test the encoding attribute. """ diagnostics = Diagnostics() self.assertFalse(diagnostics.encoding is None) self.assertNotEqual("", diagnostics.encoding) # noinspection PyStatementEffect def testMethods_005(self): """ Test the locale attribute. """ # pylint: disable=W0104 diagnostics = Diagnostics() diagnostics.locale # might not be set, so just make sure method doesn't fail def testMethods_006(self): """ Test the getValues() method. """ diagnostics = Diagnostics() values = diagnostics.getValues() self.assertEqual(diagnostics.version, values["version"]) self.assertEqual(diagnostics.interpreter, values["interpreter"]) self.assertEqual(diagnostics.platform, values["platform"]) self.assertEqual(diagnostics.encoding, values["encoding"]) self.assertEqual(diagnostics.locale, values["locale"]) self.assertEqual(diagnostics.timestamp, values["timestamp"]) def testMethods_007(self): """ Test the _buildDiagnosticLines() method. """ values = Diagnostics().getValues() lines = Diagnostics()._buildDiagnosticLines() self.assertEqual(len(values), len(lines)) def testMethods_008(self): """ Test the printDiagnostics() method. """ captureOutput(Diagnostics().printDiagnostics) def testMethods_009(self): """ Test the logDiagnostics() method. """ logger = logging.getLogger("CedarBackup3.test") Diagnostics().logDiagnostics(logger.info) def testMethods_010(self): """ Test the timestamp attribute. """ diagnostics = Diagnostics() self.assertFalse(diagnostics.timestamp is None) self.assertNotEqual("", diagnostics.timestamp) ###################### # TestFunctions class ###################### # noinspection PyUnusedLocal,PyShadowingBuiltins class TestFunctions(unittest.TestCase): """Tests for the various public functions.""" ################ # Setup methods ################ def setUp(self): try: self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): removedir(self.tmpdir) ################## # Utility methods ################## def getTempfile(self): """Gets a path to a temporary file on disk.""" (fd, name) = tempfile.mkstemp(dir=self.tmpdir) try: os.close(fd) except: pass return name def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) ################## # Test sortDict() ################## def testSortDict_001(self): """ Test for empty dictionary. """ d = {} result = sortDict(d) self.assertEqual([], result) def testSortDict_002(self): """ Test for dictionary with one item. """ d = {"a": 1} result = sortDict(d) self.assertEqual(["a"], result) def testSortDict_003(self): """ Test for dictionary with two items, same value. """ d = { "a": 1, "b": 1, } result = sortDict(d) self.assertEqual(["a", "b"], result) def testSortDict_004(self): """ Test for dictionary with two items, different values. """ d = { "a": 1, "b": 2, } result = sortDict(d) self.assertEqual(["a", "b"], result) def testSortDict_005(self): """ Test for dictionary with many items, same and different values. """ d = {"rebuild": 0, "purge": 400, "collect": 100, "validate": 0, "store": 300, "stage": 200} result = sortDict(d) self.assertEqual(["rebuild", "validate", "collect", "stage", "store", "purge"], result) ############################## # Test getFunctionReference() ############################## def testGetFunctionReference_001(self): """ Check that the search works within "standard" Python namespace. """ module = "os.path" function = "isdir" reference = getFunctionReference(module, function) self.assertTrue(isdir is reference) def testGetFunctionReference_002(self): """ Check that the search works for things within CedarBackup3. """ module = "CedarBackup3.util" function = "executeCommand" reference = getFunctionReference(module, function) self.assertTrue(executeCommand is reference) ######################## # Test resolveCommand() ######################## def testResolveCommand_001(self): """ Test that the command is echoed back unchanged when singleton is empty. """ PathResolverSingleton._instance = None command = [ "BAD", ] expected = command[:] result = resolveCommand(command) self.assertEqual(expected, result) command = [ "GOOD", ] expected = command[:] result = resolveCommand(command) self.assertEqual(expected, result) command = [ "WHATEVER", "--verbose", "--debug", "tvh:asa892831", "blech", "<", ] expected = command[:] result = resolveCommand(command) self.assertEqual(expected, result) def testResolveCommand_002(self): """ Test that the command is echoed back unchanged when mapping is not found. """ PathResolverSingleton._instance = None mappings = {"one": "/path/to/one", "two": "/path/to/two"} singleton = PathResolverSingleton() singleton.fill(mappings) command = [ "BAD", ] expected = command[:] result = resolveCommand(command) self.assertEqual(expected, result) command = [ "GOOD", ] expected = command[:] result = resolveCommand(command) self.assertEqual(expected, result) command = [ "WHATEVER", "--verbose", "--debug", "tvh:asa892831", "blech", "<", ] expected = command[:] result = resolveCommand(command) self.assertEqual(expected, result) def testResolveCommand_003(self): """ Test that the command is echoed back changed appropriately when mapping is found. """ PathResolverSingleton._instance = None mappings = {"one": "/path/to/one", "two": "/path/to/two"} singleton = PathResolverSingleton() singleton.fill(mappings) command = [ "one", ] expected = [ "/path/to/one", ] result = resolveCommand(command) self.assertEqual(expected, result) command = [ "two", ] expected = [ "/path/to/two", ] result = resolveCommand(command) self.assertEqual(expected, result) command = [ "two", "--verbose", "--debug", "tvh:asa892831", "blech", "<", ] expected = [ "/path/to/two", "--verbose", "--debug", "tvh:asa892831", "blech", "<", ] result = resolveCommand(command) self.assertEqual(expected, result) ######################## # Test executeCommand() ######################## def testExecuteCommand_001(self): """ Execute a command that should succeed, no arguments, returnOutput=False Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_002(self): """ Execute a command that should succeed, one argument, returnOutput=False Command-line: python -V """ command = [ sys.executable, ] args = [ "-V", ] (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_003(self): """ Execute a command that should succeed, two arguments, returnOutput=False Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", ] (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_004(self): """ Execute a command that should succeed, three arguments, returnOutput=False Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" first """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", "first", ] (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_005(self): """ Execute a command that should succeed, four arguments, returnOutput=False Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" first second """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", "first", "second", ] (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_006(self): """ Execute a command that should fail, returnOutput=False Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(1)" """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(1)", ] (result, output) = executeCommand(command, args, returnOutput=False) self.assertNotEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_007(self): """ Execute a command that should fail, more arguments, returnOutput=False Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(1)" first second """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(1)", "first", "second", ] (result, output) = executeCommand(command, args, returnOutput=False) self.assertNotEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_008(self): """ Execute a command that should succeed, no arguments, returnOutput=True Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(VALID_OUTPUT, output[0]) def testExecuteCommand_009(self): """ Execute a command that should succeed, one argument, returnOutput=True Command-line: python -V """ command = [ sys.executable, ] args = [ "-V", ] (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertTrue(output[0].startswith("Python")) def testExecuteCommand_010(self): """ Execute a command that should succeed, two arguments, returnOutput=True Command-line: python -c "import sys; print(''); sys.exit(0)" """ command = [ sys.executable, ] args = [ "-c", "import sys; print(''); sys.exit(0)", ] (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(os.linesep, output[0]) def testExecuteCommand_011(self): """ Execute a command that should succeed, three arguments, returnOutput=True Command-line: python -c "import sys; print('%s' % (sys.argv[1])); sys.exit(0)" first """ command = [ sys.executable, ] args = [ "-c", "import sys; print('%s' % (sys.argv[1])); sys.exit(0)", "first", ] (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) def testExecuteCommand_012(self): """ Execute a command that should succeed, four arguments, returnOutput=True Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)" first second """ command = [ sys.executable, ] args = [ "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)", "first", "second", ] (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(2, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) self.assertEqual("second%s" % os.linesep, output[1]) def testExecuteCommand_013(self): """ Execute a command that should fail, returnOutput=True Command-line: python -c "import sys; print(''); sys.exit(1)" """ command = [ sys.executable, ] args = [ "-c", "import sys; print(''); sys.exit(1)", ] (result, output) = executeCommand(command, args, returnOutput=True) self.assertNotEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(os.linesep, output[0]) def testExecuteCommand_014(self): """ Execute a command that should fail, more arguments, returnOutput=True Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)" first second """ command = [ sys.executable, ] args = [ "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)", "first", "second", ] (result, output) = executeCommand(command, args, returnOutput=True) self.assertNotEqual(0, result) self.assertEqual(2, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) self.assertEqual("second%s" % os.linesep, output[1]) def testExecuteCommand_015(self): """ Execute a command that should succeed, no arguments, returnOutput=False Do this all bundled into the command list, just to check that this works as expected. Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_016(self): """ Execute a command that should succeed, one argument, returnOutput=False Do this all bundled into the command list, just to check that this works as expected. Command-line: python -V """ command = [ sys.executable, "-V", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_017(self): """ Execute a command that should succeed, two arguments, returnOutput=False Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_018(self): """ Execute a command that should succeed, three arguments, returnOutput=False Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" first """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", "first", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_019(self): """ Execute a command that should succeed, four arguments, returnOutput=False Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" first second """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", "first", "second", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_020(self): """ Execute a command that should fail, returnOutput=False Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(1)" """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(1)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False) self.assertNotEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_021(self): """ Execute a command that should fail, more arguments, returnOutput=False Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(1)" first second """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(1)", "first", "second", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False) self.assertNotEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_022(self): """ Execute a command that should succeed, no arguments, returnOutput=True Do this all bundled into the command list, just to check that this works as expected. Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(VALID_OUTPUT, output[0]) def testExecuteCommand_023(self): """ Execute a command that should succeed, one argument, returnOutput=True Do this all bundled into the command list, just to check that this works as expected. Command-line: python -V """ command = [sys.executable, "-V"] args = [] (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertTrue(output[0].startswith("Python")) def testExecuteCommand_024(self): """ Execute a command that should succeed, two arguments, returnOutput=True Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(''); sys.exit(0)" """ command = [ sys.executable, "-c", "import sys; print(''); sys.exit(0)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(os.linesep, output[0]) def testExecuteCommand_025(self): """ Execute a command that should succeed, three arguments, returnOutput=True Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % (sys.argv[1])); sys.exit(0)" first """ command = [ sys.executable, "-c", "import sys; print('%s' % (sys.argv[1])); sys.exit(0)", "first", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) def testExecuteCommand_026(self): """ Execute a command that should succeed, four arguments, returnOutput=True Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)" first second """ command = [ sys.executable, "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)", "first", "second", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True) self.assertEqual(0, result) self.assertEqual(2, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) self.assertEqual("second%s" % os.linesep, output[1]) def testExecuteCommand_027(self): """ Execute a command that should fail, returnOutput=True Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(''); sys.exit(1)" """ command = [ sys.executable, "-c", "import sys; print(''); sys.exit(1)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True) self.assertNotEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(os.linesep, output[0]) def testExecuteCommand_028(self): """ Execute a command that should fail, more arguments, returnOutput=True Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)" first second """ command = [ sys.executable, "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)", "first", "second", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True) self.assertNotEqual(0, result) self.assertEqual(2, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) self.assertEqual("second%s" % os.linesep, output[1]) def testExecuteCommand_030(self): """ Execute a command that should succeed, no arguments, returnOutput=False, ignoring stderr. Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_031(self): """ Execute a command that should succeed, one argument, returnOutput=False, ignoring stderr. Command-line: python -V """ command = [ sys.executable, ] args = [ "-V", ] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_032(self): """ Execute a command that should succeed, two arguments, returnOutput=False, ignoring stderr. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", ] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_033(self): """ Execute a command that should succeed, three arguments, returnOutput=False, ignoring stderr. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" first """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", "first", ] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_034(self): """ Execute a command that should succeed, four arguments, returnOutput=False, ignoring stderr. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" first second """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", "first", "second", ] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_035(self): """ Execute a command that should fail, returnOutput=False, ignoring stderr. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(1)" """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(1)", ] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertNotEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_036(self): """ Execute a command that should fail, more arguments, returnOutput=False, ignoring stderr. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(1)" first second """ command = [ sys.executable, ] args = [ "-c", "import sys; print(sys.argv[1:]); sys.exit(1)", "first", "second", ] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertNotEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_037(self): """ Execute a command that should succeed, no arguments, returnOutput=True, ignoring stderr. Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(VALID_OUTPUT, output[0]) def testExecuteCommand_038(self): """ Execute a command that should succeed, one argument, returnOutput=True, ignoring stderr. Command-line: python -V """ command = [ sys.executable, ] args = [ "-c", "import sys; print('X', file=sys.stderr)", ] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=False) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual("X%s" % os.linesep, output[0]) # prove stderr is captured (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(0, len(output)) # prove stderr is now ignored def testExecuteCommand_039(self): """ Execute a command that should succeed, two arguments, returnOutput=True, ignoring stderr. Command-line: python -c "import sys; print(''); sys.exit(0)" """ command = [ sys.executable, ] args = [ "-c", "import sys; print(''); sys.exit(0)", ] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(os.linesep, output[0]) def testExecuteCommand_040(self): """ Execute a command that should succeed, three arguments, returnOutput=True, ignoring stderr. Command-line: python -c "import sys; print('%s' % (sys.argv[1])); sys.exit(0)" first """ command = [ sys.executable, ] args = [ "-c", "import sys; print('%s' % (sys.argv[1])); sys.exit(0)", "first", ] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) def testExecuteCommand_041(self): """ Execute a command that should succeed, four arguments, returnOutput=True, ignoring stderr. Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)" first second """ command = [ sys.executable, ] args = [ "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)", "first", "second", ] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(2, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) self.assertEqual("second%s" % os.linesep, output[1]) def testExecuteCommand_042(self): """ Execute a command that should fail, returnOutput=True, ignoring stderr. Command-line: python -c "import sys; print(''); sys.exit(1)" """ command = [ sys.executable, ] args = [ "-c", "import sys; print(''); sys.exit(1)", ] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertNotEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(os.linesep, output[0]) def testExecuteCommand_043(self): """ Execute a command that should fail, more arguments, returnOutput=True, ignoring stderr. Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)" first second """ command = [ sys.executable, ] args = [ "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)", "first", "second", ] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertNotEqual(0, result) self.assertEqual(2, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) self.assertEqual("second%s" % os.linesep, output[1]) def testExecuteCommand_044(self): """ Execute a command that should succeed, no arguments, returnOutput=False, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_045(self): """ Execute a command that should succeed, one argument, returnOutput=False, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -V """ command = [ sys.executable, "-V", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_046(self): """ Execute a command that should succeed, two arguments, returnOutput=False, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_047(self): """ Execute a command that should succeed, three arguments, returnOutput=False, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" first """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", "first", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_048(self): """ Execute a command that should succeed, four arguments, returnOutput=False, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(0)" first second """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(0)", "first", "second", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_049(self): """ Execute a command that should fail, returnOutput=False, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(1)" """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(1)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertNotEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_050(self): """ Execute a command that should fail, more arguments, returnOutput=False, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(sys.argv[1:]); sys.exit(1)" first second """ command = [ sys.executable, "-c", "import sys; print(sys.argv[1:]); sys.exit(1)", "first", "second", ] args = [] (result, output) = executeCommand(command, args, returnOutput=False, ignoreStderr=True) self.assertNotEqual(0, result) self.assertEqual(None, output) def testExecuteCommand_051(self): """ Execute a command that should succeed, no arguments, returnOutput=True, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(VALID_OUTPUT, output[0]) def testExecuteCommand_052(self): """ Execute a command that should succeed, one argument, returnOutput=True, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -V """ command = [ sys.executable, "-c", "import sys; print('X', file=sys.stderr)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=False) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual("X%s" % os.linesep, output[0]) # prove stderr is captured (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(0, len(output)) # prove stderr is now ignored def testExecuteCommand_053(self): """ Execute a command that should succeed, two arguments, returnOutput=True, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(''); sys.exit(0)" """ command = [ sys.executable, "-c", "import sys; print(''); sys.exit(0)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(os.linesep, output[0]) def testExecuteCommand_054(self): """ Execute a command that should succeed, three arguments, returnOutput=True, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % (sys.argv[1])); sys.exit(0)" first """ command = [ sys.executable, "-c", "import sys; print('%s' % (sys.argv[1])); sys.exit(0)", "first", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) def testExecuteCommand_055(self): """ Execute a command that should succeed, four arguments, returnOutput=True, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)" first second """ command = [ sys.executable, "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)", "first", "second", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertEqual(0, result) self.assertEqual(2, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) self.assertEqual("second%s" % os.linesep, output[1]) def testExecuteCommand_056(self): """ Execute a command that should fail, returnOutput=True, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(''); sys.exit(1)" """ command = [ sys.executable, "-c", "import sys; print(''); sys.exit(1)", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertNotEqual(0, result) self.assertEqual(1, len(output)) self.assertEqual(os.linesep, output[0]) def testExecuteCommand_057(self): """ Execute a command that should fail, more arguments, returnOutput=True, ignoring stderr. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)" first second """ command = [ sys.executable, "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)", "first", "second", ] args = [] (result, output) = executeCommand(command, args, returnOutput=True, ignoreStderr=True) self.assertNotEqual(0, result) self.assertEqual(2, len(output)) self.assertEqual("first%s" % os.linesep, output[0]) self.assertEqual("second%s" % os.linesep, output[1]) def testExecuteCommand_058(self): """ Execute a command that should succeed, no arguments, returnOutput=False, using outputFile. Do this all bundled into the command list, just to check that this works as expected. Command-line: non-Python platform-specific valid command """ command = VALID_COMMAND args = VALID_ARGS filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) self.assertTrue(os.path.exists(filename)) with open(filename) as f: output = f.readlines() self.assertEqual(1, len(output)) self.assertEqual( VALID_OUTPUT, output[0].replace("\n", os.linesep) ) # when reading from a file, Python translates the line ending def testExecuteCommand_059(self): """ Execute a command that should succeed, one argument, returnOutput=False, using outputFile. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -V """ command = [sys.executable, "-V"] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) self.assertTrue(os.path.exists(filename)) with open(filename) as f: output = f.readlines() self.assertEqual(1, len(output)) self.assertTrue(output[0].startswith("Python")) def testExecuteCommand_060(self): """ Execute a command that should succeed, two arguments, returnOutput=False, using outputFile. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(''); sys.exit(0)" """ command = [ sys.executable, "-c", "import sys; print(''); sys.exit(0)", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) self.assertTrue(os.path.exists(filename)) with open(filename) as f: output = f.readlines() self.assertEqual(1, len(output)) self.assertEqual("\n", output[0]) def testExecuteCommand_061(self): """ Execute a command that should succeed, three arguments, returnOutput=False, using outputFile. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % (sys.argv[1])); sys.exit(0)" first """ command = [ sys.executable, "-c", "import sys; print('%s' % (sys.argv[1])); sys.exit(0)", "first", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) self.assertTrue(os.path.exists(filename)) with open(filename) as f: output = f.readlines() self.assertEqual(1, len(output)) self.assertEqual("first\n", output[0]) def testExecuteCommand_062(self): """ Execute a command that should succeed, four arguments, returnOutput=False, using outputFile. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)" first second """ command = [ sys.executable, "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(0)", "first", "second", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) self.assertTrue(os.path.exists(filename)) with open(filename) as f: output = f.readlines() self.assertEqual(2, len(output)) self.assertEqual("first\n", output[0]) self.assertEqual("second\n", output[1]) def testExecuteCommand_063(self): """ Execute a command that should fail, returnOutput=False, using outputFile. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print(''); sys.exit(1)" """ command = [ sys.executable, "-c", "import sys; print(''); sys.exit(1)", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, returnOutput=False, outputFile=outputFile)[0] self.assertNotEqual(0, result) self.assertTrue(os.path.exists(filename)) with open(filename) as f: output = f.readlines() self.assertEqual(1, len(output)) self.assertEqual("\n", output[0]) def testExecuteCommand_064(self): """ Execute a command that should fail, more arguments, returnOutput=False, using outputFile. Do this all bundled into the command list, just to check that this works as expected. Command-line: python -c "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)" first second """ command = [ sys.executable, "-c", "import sys; print('%s' % sys.argv[1]); print('%s' % sys.argv[2]); sys.exit(1)", "first", "second", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, returnOutput=False, outputFile=outputFile)[0] self.assertNotEqual(0, result) self.assertTrue(os.path.exists(filename)) with open(filename) as f: output = f.readlines() self.assertEqual(2, len(output)) self.assertEqual("first\n", output[0]) self.assertEqual("second\n", output[1]) def testExecuteCommand_065(self): """ Execute a command with a huge amount of output all on stdout. The output should contain only data on stdout, and ignoreStderr should be True. This test helps confirm that the function doesn't hang when there is either a lot of data or a lot of data to ignore. """ lotsoflines = self.resources["lotsoflines.py"] command = [ sys.executable, lotsoflines, "stdout", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, ignoreStderr=True, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) length = 0 with open(filename) as contents: for i in contents: length += 1 self.assertEqual(100000, length) def testExecuteCommand_066(self): """ Execute a command with a huge amount of output all on stdout. The output should contain only data on stdout, and ignoreStderr should be False. This test helps confirm that the function doesn't hang when there is either a lot of data or a lot of data to ignore. """ lotsoflines = self.resources["lotsoflines.py"] command = [ sys.executable, lotsoflines, "stdout", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, ignoreStderr=False, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) length = 0 with open(filename) as contents: for i in contents: length += 1 self.assertEqual(100000, length) def testExecuteCommand_067(self): """ Execute a command with a huge amount of output all on stdout. The output should contain only data on stderr, and ignoreStderr should be True. This test helps confirm that the function doesn't hang when there is either a lot of data or a lot of data to ignore. """ lotsoflines = self.resources["lotsoflines.py"] command = [ sys.executable, lotsoflines, "stderr", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, ignoreStderr=True, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) length = 0 with open(filename) as contents: for i in contents: length += 1 self.assertEqual(0, length) def testExecuteCommand_068(self): """ Execute a command with a huge amount of output all on stdout. The output should contain only data on stdout, and ignoreStderr should be False. This test helps confirm that the function doesn't hang when there is either a lot of data or a lot of data to ignore. """ lotsoflines = self.resources["lotsoflines.py"] command = [ sys.executable, lotsoflines, "stderr", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, ignoreStderr=False, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) length = 0 with open(filename) as contents: for i in contents: length += 1 self.assertEqual(100000, length) def testExecuteCommand_069(self): """ Execute a command with a huge amount of output all on stdout. The output should contain data on stdout and stderr, and ignoreStderr should be True. This test helps confirm that the function doesn't hang when there is either a lot of data or a lot of data to ignore. """ lotsoflines = self.resources["lotsoflines.py"] command = [ sys.executable, lotsoflines, "both", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, ignoreStderr=True, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) length = 0 with open(filename) as contents: for i in contents: length += 1 self.assertEqual(100000, length) def testExecuteCommand_070(self): """ Execute a command with a huge amount of output all on stdout. The output should contain data on stdout and stderr, and ignoreStderr should be False. This test helps confirm that the function doesn't hang when there is either a lot of data or a lot of data to ignore. """ lotsoflines = self.resources["lotsoflines.py"] command = [ sys.executable, lotsoflines, "both", ] args = [] filename = self.getTempfile() with open(filename, "wb") as outputFile: result = executeCommand(command, args, ignoreStderr=False, returnOutput=False, outputFile=outputFile)[0] self.assertEqual(0, result) length = 0 with open(filename) as contents: for i in contents: length += 1 self.assertEqual(100000 * 2, length) #################### # Test encodePath() #################### def testEncodePath_002(self): """ Test with a simple string, empty. """ path = "" safePath = encodePath(path) self.assertTrue(isinstance(safePath, str)) self.assertEqual(path, safePath) def testEncodePath_003(self): """ Test with an simple string, an ascii word. """ path = "whatever" safePath = encodePath(path) self.assertTrue(isinstance(safePath, str)) self.assertEqual(path, safePath) def testEncodePath_004(self): """ Test with simple string, a complete path. """ path = "/usr/share/doc/xmltv/README.Debian" safePath = encodePath(path) self.assertTrue(isinstance(safePath, str)) self.assertEqual(path, safePath) def testEncodePath_005(self): """ Test with simple string, a non-ascii path. """ path = "\xe2\x99\xaa\xe2\x99\xac" safePath = encodePath(path) self.assertTrue(isinstance(safePath, str)) self.assertEqual(path, safePath) def testEncodePath_006(self): """ Test with a simple string, empty. """ path = "" safePath = encodePath(path) self.assertTrue(isinstance(safePath, str)) self.assertEqual(path, safePath) def testEncodePath_007(self): """ Test with an simple string, an ascii word. """ path = "whatever" safePath = encodePath(path) self.assertTrue(isinstance(safePath, str)) self.assertEqual(path, safePath) def testEncodePath_008(self): """ Test with simple string, a complete path. """ path = "/usr/share/doc/xmltv/README.Debian" safePath = encodePath(path) self.assertTrue(isinstance(safePath, str)) self.assertEqual(path, safePath) def testEncodePath_009(self): """ Test with simple string, a non-ascii path. """ path = "\xe2\x99\xaa\xe2\x99\xac" safePath = encodePath(path) self.assertTrue(isinstance(safePath, str)) self.assertEqual("\xe2\x99\xaa\xe2\x99\xac", safePath) ##################### # Test convertSize() ###################### def testConvertSize_001(self): """ Test valid conversion from bytes to bytes. """ fromUnit = UNIT_BYTES toUnit = UNIT_BYTES size = 10.0 result = convertSize(size, fromUnit, toUnit) self.assertEqual(result, size) def testConvertSize_002(self): """ Test valid conversion from sectors to bytes and back. """ fromUnit = UNIT_SECTORS toUnit = UNIT_BYTES size = 10 result1 = convertSize(size, fromUnit, toUnit) self.assertEqual(10 * 2048, result1) result2 = convertSize(result1, toUnit, fromUnit) self.assertEqual(result2, size) def testConvertSize_003(self): """ Test valid conversion from kbytes to bytes and back. """ fromUnit = UNIT_KBYTES toUnit = UNIT_BYTES size = 10 result1 = convertSize(size, fromUnit, toUnit) self.assertEqual(10 * 1024, result1) result2 = convertSize(result1, toUnit, fromUnit) self.assertEqual(result2, size) def testConvertSize_004(self): """ Test valid conversion from mbytes to bytes and back. """ fromUnit = UNIT_MBYTES toUnit = UNIT_BYTES size = 10 result1 = convertSize(size, fromUnit, toUnit) self.assertEqual(10 * 1024 * 1024, result1) result2 = convertSize(result1, toUnit, fromUnit) self.assertEqual(result2, size) def testConvertSize_005(self): """ Test valid conversion from gbytes to bytes and back. """ fromUnit = UNIT_GBYTES toUnit = UNIT_BYTES size = 10 result1 = convertSize(size, fromUnit, toUnit) self.assertEqual(10 * 1024 * 1024 * 1024, result1) result2 = convertSize(result1, toUnit, fromUnit) self.assertEqual(result2, size) def testConvertSize_006(self): """ Test valid conversion from mbytes to kbytes and back. """ fromUnit = UNIT_MBYTES toUnit = UNIT_KBYTES size = 10 result1 = convertSize(size, fromUnit, toUnit) self.assertEqual(size * 1024, result1) result2 = convertSize(result1, toUnit, fromUnit) self.assertEqual(result2, size) def testConvertSize_007(self): """ Test with an invalid from unit (None). """ fromUnit = None toUnit = UNIT_BYTES size = 10 self.assertRaises(ValueError, convertSize, size, fromUnit, toUnit) def testConvertSize_008(self): """ Test with an invalid from unit. """ fromUnit = 333 toUnit = UNIT_BYTES size = 10 self.assertRaises(ValueError, convertSize, size, fromUnit, toUnit) def testConvertSize_009(self): """ Test with an invalid to unit (None) """ fromUnit = UNIT_BYTES toUnit = None size = 10 self.assertRaises(ValueError, convertSize, size, fromUnit, toUnit) def testConvertSize_010(self): """ Test with an invalid to unit. """ fromUnit = UNIT_BYTES toUnit = "ken" size = 10 self.assertRaises(ValueError, convertSize, size, fromUnit, toUnit) def testConvertSize_011(self): """ Test with an invalid quantity (None) """ fromUnit = UNIT_BYTES toUnit = UNIT_BYTES size = None self.assertRaises(ValueError, convertSize, size, fromUnit, toUnit) def testConvertSize_012(self): """ Test with an invalid quantity (not a floating point). """ fromUnit = UNIT_BYTES toUnit = UNIT_BYTES size = "blech" self.assertRaises(ValueError, convertSize, size, fromUnit, toUnit) #################### # Test nullDevice() ##################### def testNullDevice_001(self): """ Test that the function behaves sensibly. """ device = nullDevice() if sys.platform == "win32": self.assertEqual("nul", device) else: self.assertEqual("/dev/null", device) ###################### # Test displayBytes() ###################### def testDisplayBytes_001(self): """ Test display for a positive value < 1 KB """ bytes = 12 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("12 bytes", result) result = displayBytes(bytes, 3) self.assertEqual("12 bytes", result) def testDisplayBytes_002(self): """ Test display for a negative value < 1 KB """ bytes = -12 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("-12 bytes", result) result = displayBytes(bytes, 3) self.assertEqual("-12 bytes", result) def testDisplayBytes_003(self): """ Test display for a positive value = 1kB """ bytes = 1024 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("1.00 kB", result) result = displayBytes(bytes, 3) self.assertEqual("1.000 kB", result) def testDisplayBytes_004(self): """ Test display for a positive value >= 1kB """ bytes = 5678 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("5.54 kB", result) result = displayBytes(bytes, 3) self.assertEqual("5.545 kB", result) def testDisplayBytes_005(self): """ Test display for a negative value >= 1kB """ bytes = -5678 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("-5.54 kB", result) result = displayBytes(bytes, 3) self.assertEqual("-5.545 kB", result) def testDisplayBytes_006(self): """ Test display for a positive value = 1MB """ bytes = 1024.0 * 1024.0 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("1.00 MB", result) result = displayBytes(bytes, 3) self.assertEqual("1.000 MB", result) def testDisplayBytes_007(self): """ Test display for a positive value >= 1MB """ bytes = 72372224 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("69.02 MB", result) result = displayBytes(bytes, 3) self.assertEqual("69.020 MB", result) def testDisplayBytes_008(self): """ Test display for a negative value >= 1MB """ bytes = -72372224.0 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("-69.02 MB", result) result = displayBytes(bytes, 3) self.assertEqual("-69.020 MB", result) def testDisplayBytes_009(self): """ Test display for a positive value = 1GB """ bytes = 1024.0 * 1024.0 * 1024.0 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("1.00 GB", result) result = displayBytes(bytes, 3) self.assertEqual("1.000 GB", result) def testDisplayBytes_010(self): """ Test display for a positive value >= 1GB """ bytes = 4.4 * 1024.0 * 1024.0 * 1024.0 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("4.40 GB", result) result = displayBytes(bytes, 3) self.assertEqual("4.400 GB", result) def testDisplayBytes_011(self): """ Test display for a negative value >= 1GB """ bytes = -1234567891011 # pylint: disable=W0622 result = displayBytes(bytes) self.assertEqual("-1149.78 GB", result) result = displayBytes(bytes, 3) self.assertEqual("-1149.781 GB", result) def testDisplayBytes_012(self): """ Test display with an invalid quantity (None). """ bytes = None # pylint: disable=W0622 self.assertRaises(ValueError, displayBytes, bytes) def testDisplayBytes_013(self): """ Test display with an invalid quantity (not a floating point). """ bytes = "ken" # pylint: disable=W0622 self.assertRaises(ValueError, displayBytes, bytes) ######################### # Test deriveDayOfWeek() ######################### def testDeriveDayOfWeek_001(self): """ Test for valid day names. """ self.assertEqual(0, deriveDayOfWeek("monday")) self.assertEqual(1, deriveDayOfWeek("tuesday")) self.assertEqual(2, deriveDayOfWeek("wednesday")) self.assertEqual(3, deriveDayOfWeek("thursday")) self.assertEqual(4, deriveDayOfWeek("friday")) self.assertEqual(5, deriveDayOfWeek("saturday")) self.assertEqual(6, deriveDayOfWeek("sunday")) def testDeriveDayOfWeek_002(self): """ Test for invalid day names. """ self.assertEqual(-1, deriveDayOfWeek("bogus")) ####################### # Test isStartOfWeek() ####################### # noinspection PyUnboundLocalVariable def testIsStartOfWeek001(self): """ Test positive case. """ day = time.localtime().tm_wday if day == 0: result = isStartOfWeek("monday") elif day == 1: result = isStartOfWeek("tuesday") elif day == 2: result = isStartOfWeek("wednesday") elif day == 3: result = isStartOfWeek("thursday") elif day == 4: result = isStartOfWeek("friday") elif day == 5: result = isStartOfWeek("saturday") elif day == 6: result = isStartOfWeek("sunday") self.assertEqual(True, result) # noinspection PyUnboundLocalVariable def testIsStartOfWeek002(self): """ Test negative case. """ day = time.localtime().tm_wday if day == 0: result = isStartOfWeek("friday") elif day == 1: result = isStartOfWeek("saturday") elif day == 2: result = isStartOfWeek("sunday") elif day == 3: result = isStartOfWeek("monday") elif day == 4: result = isStartOfWeek("tuesday") elif day == 5: result = isStartOfWeek("wednesday") elif day == 6: result = isStartOfWeek("thursday") self.assertEqual(False, result) ############################# # Test buildNormalizedPath() ############################# def testBuildNormalizedPath001(self): """ Test for a None path. """ self.assertRaises(ValueError, buildNormalizedPath, None) def testBuildNormalizedPath002(self): """ Test for an empty path. """ path = "" expected = "" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath003(self): """ Test for "." """ path = "." expected = "_" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath004(self): """ Test for ".." """ path = ".." expected = "_." actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath005(self): """ Test for "..........." """ path = ".........." expected = "_........." actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath006(self): """ Test for "/" """ path = "/" expected = "_" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath007(self): """ Test for "\\" """ path = "\\" expected = "_" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath008(self): """ Test for "/." """ path = "/." expected = "_" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath009(self): """ Test for "/.." """ path = "/.." expected = "_." actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath010(self): """ Test for "/..." """ path = "/..." expected = "_.." actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath011(self): r""" Test for "\." """ path = r"\." expected = "_" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath012(self): r""" Test for "\.." """ path = r"\.." expected = "_." actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath013(self): r""" Test for "\..." """ path = r"\..." expected = "_.." actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath014(self): """ Test for "/var/log/apache/httpd.log.1" """ path = "/var/log/apache/httpd.log.1" expected = "var-log-apache-httpd.log.1" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath015(self): """ Test for "var/log/apache/httpd.log.1" """ path = "var/log/apache/httpd.log.1" expected = "var-log-apache-httpd.log.1" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath016(self): """ Test for "\\var/log/apache\\httpd.log.1" """ path = "\\var/log/apache\\httpd.log.1" expected = "var-log-apache-httpd.log.1" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath017(self): """ Test for "/Big Nasty Base Path With Spaces/something/else/space s/file. log .2 ." """ path = "/Big Nasty Base Path With Spaces/something/else/space s/file. log .2 ." expected = "Big_Nasty_Base_Path_With_Spaces-something-else-space_s-file.__log___.2_." actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath018(self): """ Test for Windows path with drive letter and slashes. """ path = "c:/Users/pronovic/projects/repos" expected = "c-Users-pronovic-projects-repos" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath019(self): """ Test for Windows path with drive letter and backslashes. """ path = r"c:\Users\pronovic\projects\repos" expected = "c-Users-pronovic-projects-repos" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) def testBuildNormalizedPath020(self): """ Test for path with embedded colon. """ path = "/path/to/whatever/key:value" expected = "path-to-whatever-key_value" actual = buildNormalizedPath(path) self.assertEqual(expected, actual) ########################## # Test splitCommandLine() ########################## def testSplitCommandLine_001(self): """ Test for a None command line. """ commandLine = None self.assertRaises(ValueError, splitCommandLine, commandLine) def testSplitCommandLine_002(self): """ Test for an empty command line. """ commandLine = "" result = splitCommandLine(commandLine) self.assertEqual([], result) def testSplitCommandLine_003(self): """ Test for a command line with no quoted arguments. """ commandLine = "cback --verbose stage store purge" result = splitCommandLine(commandLine) self.assertEqual(["cback", "--verbose", "stage", "store", "purge"], result) def testSplitCommandLine_004(self): """ Test for a command line with double-quoted arguments. """ commandLine = 'cback "this is a really long double-quoted argument"' result = splitCommandLine(commandLine) self.assertEqual(["cback", "this is a really long double-quoted argument"], result) def testSplitCommandLine_005(self): """ Test for a command line with single-quoted arguments. """ commandLine = "cback 'this is a really long single-quoted argument'" result = splitCommandLine(commandLine) self.assertEqual(["cback", "'this", "is", "a", "really", "long", "single-quoted", "argument'"], result) ######################### # Test dereferenceLink() ######################### @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testDereferenceLink_001(self): """ Test for a path that is a link, absolute=false. """ self.extractTar("tree10") path = self.buildPath(["tree10", "link002"]) expected = "file002" actual = dereferenceLink(path, absolute=False) self.assertEqual(expected, actual) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testDereferenceLink_002(self): """ Test for a path that is a link, absolute=true. """ self.extractTar("tree10") path = self.buildPath(["tree10", "link002"]) expected = self.buildPath(["tree10", "file002"]) actual = dereferenceLink(path) self.assertEqual(expected, actual) actual = dereferenceLink(path, absolute=True) self.assertEqual(expected, actual) def testDereferenceLink_003(self): """ Test for a path that is a file (not a link), absolute=false. """ self.extractTar("tree10") path = self.buildPath(["tree10", "file001"]) expected = path actual = dereferenceLink(path, absolute=False) self.assertEqual(expected, actual) def testDereferenceLink_004(self): """ Test for a path that is a file (not a link), absolute=true. """ self.extractTar("tree10") path = self.buildPath(["tree10", "file001"]) expected = path actual = dereferenceLink(path) self.assertEqual(expected, actual) actual = dereferenceLink(path, absolute=True) self.assertEqual(expected, actual) def testDereferenceLink_005(self): """ Test for a path that is a directory (not a link), absolute=false. """ self.extractTar("tree10") path = self.buildPath(["tree10", "dir001"]) expected = path actual = dereferenceLink(path, absolute=False) self.assertEqual(expected, actual) def testDereferenceLink_006(self): """ Test for a path that is a directory (not a link), absolute=true. """ self.extractTar("tree10") path = self.buildPath(["tree10", "dir001"]) expected = path actual = dereferenceLink(path) self.assertEqual(expected, actual) actual = dereferenceLink(path, absolute=True) self.assertEqual(expected, actual) def testDereferenceLink_007(self): """ Test for a path that does not exist, absolute=false. """ self.extractTar("tree10") path = self.buildPath(["tree10", "blech"]) expected = path actual = dereferenceLink(path, absolute=False) self.assertEqual(expected, actual) def testDereferenceLink_008(self): """ Test for a path that does not exist, absolute=true. """ self.extractTar("tree10") path = self.buildPath(["tree10", "blech"]) expected = path actual = dereferenceLink(path) self.assertEqual(expected, actual) actual = dereferenceLink(path, absolute=True) self.assertEqual(expected, actual) ################################### # Test parseCommaSeparatedString() ################################### def testParseCommaSeparatedString_001(self): """ Test parseCommaSeparatedString() for a None string. """ actual = parseCommaSeparatedString(None) self.assertEqual(None, actual) def testParseCommaSeparatedString_002(self): """ Test parseCommaSeparatedString() for an empty string. """ actual = parseCommaSeparatedString("") self.assertEqual([], actual) def testParseCommaSeparatedString_003(self): """ Test parseCommaSeparatedString() for a string with one value. """ actual = parseCommaSeparatedString("ken") self.assertEqual(["ken"], actual) def testParseCommaSeparatedString_004(self): """ Test parseCommaSeparatedString() for a string with multiple values, no spaces. """ actual = parseCommaSeparatedString("a,b,c") self.assertEqual(["a", "b", "c"], actual) def testParseCommaSeparatedString_005(self): """ Test parseCommaSeparatedString() for a string with multiple values, with spaces. """ actual = parseCommaSeparatedString("a, b, c") self.assertEqual(["a", "b", "c"], actual) def testParseCommaSeparatedString_006(self): """ Test parseCommaSeparatedString() for a string with multiple values, worst-case kind of value. """ actual = parseCommaSeparatedString(" one, two,three, four , five , six, seven,,eight ,") self.assertEqual(["one", "two", "three", "four", "five", "six", "seven", "eight"], actual) ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1708919263.1649563 cedar_backup3-3.8.1/tests/test_writers_util.py0000644000000000000000000021205714567004737016472 0ustar00# -*- coding: utf-8 -*- # vim: set ft=python ts=4 sw=4 expandtab: # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # C E D A R # S O L U T I O N S "Software done right." # S O F T W A R E # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Copyright (c) 2004-2007,2010,2011,2015 Kenneth J. Pronovici. # All rights reserved. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License, # Version 2, as published by the Free Software Foundation. # # 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. # # Copies of the GNU General Public License are available from # the Free Software Foundation website, http://www.gnu.org/. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # Author : Kenneth J. Pronovici # Language : Python 3 # Project : Cedar Backup, release 3 # Purpose : Tests writer utility functionality. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # ######################################################################## # Module documentation ######################################################################## """ Unit tests for CedarBackup3/writers/util.py. Code Coverage ============= This module contains individual tests for the public functions and classes implemented in writers/util.py. I usually prefer to test only the public interface to a class, because that way the regression tests don't depend on the internal implementation. In this case, I've decided to test some of the private methods, because their "privateness" is more a matter of presenting a clean external interface than anything else (most of the private methods are static). Being able to test these methods also makes it easier to gain some reasonable confidence in the code even if some tests are not run because WRITERSUTILTESTS_FULL is not set to "Y" in the environment (see below). Naming Conventions ================== I prefer to avoid large unit tests which validate more than one piece of functionality, and I prefer to avoid using overly descriptive (read: long) test names, as well. Instead, I use lots of very small tests that each validate one specific thing. These small tests are then named with an index number, yielding something like ``testAddDir_001`` or ``testValidate_010``. Each method has a docstring describing what it's supposed to accomplish. I feel that this makes it easier to judge how important a given failure is, and also makes it somewhat easier to diagnose and fix individual problems. Full vs. Reduced Tests ====================== Some Cedar Backup regression tests require a specialized environment in order to run successfully. This environment won't necessarily be available on every build system out there (for instance, on a Debian autobuilder). Because of this, the default behavior is to run a "reduced feature set" test suite that has no surprising system, kernel or network requirements. If you want to run all of the tests, set WRITERSUTILTESTS_FULL to "Y" in the environment. In this module, there are three dependencies: the system must have ``mkisofs`` installed, the kernel must allow ISO images to be mounted in-place via a loopback mechanism, and the current user must be allowed (via ``sudo``) to mount and unmount such loopback filesystems. See documentation by the :any:`TestIsoImage.mountImage` and :any:`TestIsoImage.unmountImage` methods for more information on what ``sudo`` access is required. @author Kenneth J. Pronovici """ ######################################################################## # Import modules and do runtime validations ######################################################################## import os import tempfile import time import unittest from CedarBackup3.filesystem import FilesystemList from CedarBackup3.testutil import ( buildPath, configureLogging, extractTar, findResources, platformMacOsX, platformSupportsLinks, removedir, setupOverrides, ) from CedarBackup3.util import executeCommand, pathJoin from CedarBackup3.writers.util import IsoImage, validateDriveSpeed, validateScsiId ####################################################################### # Module-wide configuration and constants ####################################################################### DATA_DIRS = ["./data", "./tests/data"] RESOURCES = ["tree9.tar.gz"] SUDO_CMD = ["sudo"] HDIUTIL_CMD = ["hdiutil"] GCONF_CMD = ["gconftool-2"] INVALID_FILE = "bogus" # This file name should never exist ####################################################################### # Utility functions ####################################################################### def runAllTests(): """Returns true/false depending on whether the full test suite should be run.""" if "WRITERSUTILTESTS_FULL" in os.environ: return os.environ["WRITERSUTILTESTS_FULL"] == "Y" else: return False ####################################################################### # Test Case Classes ####################################################################### ###################### # TestFunctions class ###################### class TestFunctions(unittest.TestCase): """Tests for the various public functions.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() # We absolutely need the overrides set properly for this test, since it # runs programs. Since other tests might mess with the overrides and/or # singletons, and we don't control the order of execution, we need to set # them up here. setupOverrides() def setUp(self): pass def tearDown(self): pass ######################## # Test validateScsiId() ######################## def testValidateScsiId_001(self): """ Test with simple scsibus,target,lun address. """ scsiId = "0,0,0" result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_002(self): """ Test with simple scsibus,target,lun address containing spaces. """ scsiId = " 0, 0, 0 " result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_003(self): """ Test with simple ATA address. """ scsiId = "ATA:3,2,1" result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_004(self): """ Test with simple ATA address containing spaces. """ scsiId = "ATA: 3, 2,1 " result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_005(self): """ Test with simple ATAPI address. """ scsiId = "ATAPI:1,2,3" result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_006(self): """ Test with simple ATAPI address containing spaces. """ scsiId = " ATAPI:1, 2, 3" result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_007(self): """ Test with default-device Mac address. """ scsiId = "IOCompactDiscServices" result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_008(self): """ Test with an alternate-device Mac address. """ scsiId = "IOCompactDiscServices/2" result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_009(self): """ Test with an alternate-device Mac address. """ scsiId = "IOCompactDiscServices/12" result = validateScsiId(scsiId) self.assertEqual(scsiId, result) def testValidateScsiId_010(self): """ Test with an invalid address with a missing field. """ scsiId = "1,2" self.assertRaises(ValueError, validateScsiId, scsiId) def testValidateScsiId_011(self): """ Test with an invalid Mac-style address with a backslash. """ scsiId = "IOCompactDiscServices\\3" self.assertRaises(ValueError, validateScsiId, scsiId) def testValidateScsiId_012(self): """ Test with an invalid address with an invalid prefix separator. """ scsiId = "ATAPI;1,2,3" self.assertRaises(ValueError, validateScsiId, scsiId) def testValidateScsiId_013(self): """ Test with an invalid address with an invalid prefix separator. """ scsiId = "ATA-1,2,3" self.assertRaises(ValueError, validateScsiId, scsiId) def testValidateScsiId_014(self): """ Test with a None SCSI id. """ scsiId = None result = validateScsiId(scsiId) self.assertEqual(scsiId, result) ############################ # Test validateDriveSpeed() ############################ def testValidateDriveSpeed_001(self): """ Test for a valid drive speed. """ speed = 1 result = validateDriveSpeed(speed) self.assertEqual(result, speed) speed = 2 result = validateDriveSpeed(speed) self.assertEqual(result, speed) speed = 30 result = validateDriveSpeed(speed) self.assertEqual(result, speed) speed = 2.0 result = validateDriveSpeed(speed) self.assertEqual(result, speed) speed = 1.3 result = validateDriveSpeed(speed) self.assertEqual(result, 1) # truncated def testValidateDriveSpeed_002(self): """ Test for a None drive speed (special case). """ speed = None result = validateDriveSpeed(speed) self.assertEqual(result, speed) def testValidateDriveSpeed_003(self): """ Test for an invalid drive speed (zero) """ speed = 0 self.assertRaises(ValueError, validateDriveSpeed, speed) def testValidateDriveSpeed_004(self): """ Test for an invalid drive speed (negative) """ speed = -1 self.assertRaises(ValueError, validateDriveSpeed, speed) def testValidateDriveSpeed_005(self): """ Test for an invalid drive speed (not integer) """ speed = "ken" self.assertRaises(ValueError, validateDriveSpeed, speed) ##################### # TestIsoImage class ##################### class TestIsoImage(unittest.TestCase): """Tests for the IsoImage class.""" ################ # Setup methods ################ @classmethod def setUpClass(cls): configureLogging() def setUp(self): try: self.disableGnomeAutomount() self.mounted = False self.tmpdir = tempfile.mkdtemp() self.resources = findResources(RESOURCES, DATA_DIRS) except Exception as e: self.fail(e) def tearDown(self): if self.mounted: self.unmountImage() removedir(self.tmpdir) self.enableGnomeAutomount() ################## # Utility methods ################## def extractTar(self, tarname): """Extracts a tarfile with a particular name.""" extractTar(self.tmpdir, self.resources["%s.tar.gz" % tarname]) def buildPath(self, components): """Builds a complete search path from a list of components.""" components.insert(0, self.tmpdir) return buildPath(components) def mountImage(self, imagePath): """ Mounts an ISO image at ``self.tmpdir/mnt`` using loopback. This function chooses the correct operating system-specific function and calls it. If there is no operating-system-specific function, we fall back to the generic function, which uses 'sudo mount'. Returns: Path the image is mounted at Raises: IOError: If the command cannot be executed """ if platformMacOsX(): return self.mountImageDarwin(imagePath) else: return self.mountImageGeneric(imagePath) def mountImageDarwin(self, imagePath): """ Mounts an ISO image at ``self.tmpdir/mnt`` using Darwin's ``hdiutil`` program. Darwin (Mac OS X) uses the ``hdiutil`` program to mount volumes. The mount command doesn't really exist (or rather, doesn't know what to do with ISO 9660 volumes). *Note:* According to the manpage, the mountpoint path can't be any longer than MNAMELEN characters (currently 90?) so you might have problems with this depending on how your test environment is set up. Returns: Path the image is mounted at Raises: IOError: If the command cannot be executed """ mountPath = self.buildPath(["mnt"]) os.mkdir(mountPath) args = [ "attach", "-mountpoint", mountPath, imagePath, ] (result, output) = executeCommand(HDIUTIL_CMD, args, returnOutput=True) if result != 0: raise IOError("Error (%d) executing command to mount image." % result) self.mounted = True return mountPath def mountImageGeneric(self, imagePath): """ Mounts an ISO image at ``self.tmpdir/mnt`` using loopback. Note that this will fail unless the user has been granted permissions via sudo, using something like this: Cmnd_Alias LOOPMOUNT = /bin/mount -d -t iso9660 -o loop * * Keep in mind that this entry is a security hole, so you might not want to keep it in ``/etc/sudoers`` all of the time. Returns: Path the image is mounted at Raises: IOError: If the command cannot be executed """ mountPath = self.buildPath(["mnt"]) os.mkdir(mountPath) args = [ "mount", "-t", "iso9660", "-o", "loop", imagePath, mountPath, ] (result, output) = executeCommand(SUDO_CMD, args, returnOutput=True) if result != 0: raise IOError("Error (%d) executing command to mount image." % result) self.mounted = True return mountPath def unmountImage(self): """ Unmounts an ISO image from ``self.tmpdir/mnt``. This function chooses the correct operating system-specific function and calls it. If there is no operating-system-specific function, we fall back to the generic function, which uses 'sudo unmount'. Raises: IOError: If the command cannot be executed """ if platformMacOsX(): self.unmountImageDarwin() else: self.unmountImageGeneric() def unmountImageDarwin(self): """ Unmounts an ISO image from ``self.tmpdir/mnt`` using Darwin's ``hdiutil`` program. Darwin (Mac OS X) uses the ``hdiutil`` program to mount volumes. The mount command doesn't really exist (or rather, doesn't know what to do with ISO 9660 volumes). *Note:* According to the manpage, the mountpoint path can't be any longer than MNAMELEN characters (currently 90?) so you might have problems with this depending on how your test environment is set up. Raises: IOError: If the command cannot be executed """ mountPath = self.buildPath(["mnt"]) args = [ "detach", mountPath, ] (result, output) = executeCommand(HDIUTIL_CMD, args, returnOutput=True) if result != 0: raise IOError("Error (%d) executing command to unmount image." % result) self.mounted = False def unmountImageGeneric(self): """ Unmounts an ISO image from ``self.tmpdir/mnt``. Sometimes, multiple tries are needed because the ISO filesystem is still in use. We try twice with a 1-second pause between attempts. If this isn't successful, you may run out of loopback devices. Check for leftover mounts using 'losetup -a' as root. You can remove a leftover mount using something like 'losetup -d /dev/loop0'. Note that this will fail unless the user has been granted permissions via sudo, using something like this: Cmnd_Alias LOOPUNMOUNT = /bin/umount -d -t iso9660 * Keep in mind that this entry is a security hole, so you might not want to keep it in ``/etc/sudoers`` all of the time. Raises: IOError: If the command cannot be executed """ mountPath = self.buildPath(["mnt"]) args = [ "umount", "-d", "-t", "iso9660", mountPath, ] (result, output) = executeCommand(SUDO_CMD, args, returnOutput=True) if result != 0: time.sleep(1) (result, output) = executeCommand(SUDO_CMD, args, returnOutput=True) if result != 0: raise IOError("Error (%d) executing command to unmount image." % result) self.mounted = False def disableGnomeAutomount(self): """ Disables GNOME auto-mounting of ISO volumes when full tests are enabled. As of this writing (October 2011), recent versions of GNOME in Debian come pre-configured to auto-mount various kinds of media (like CDs and thumb drives). Besides auto-mounting the media, GNOME also often opens up a Nautilus browser window to explore the newly-mounted media. This causes lots of problems for these unit tests, which assume that they have complete control over the mounting and unmounting process. So, for these tests to work, we need to disable GNOME auto-mounting. """ self.origMediaAutomount = None self.origMediaAutomountOpen = None if runAllTests(): args = [ "--get", "/apps/nautilus/preferences/media_automount", ] (result, output) = executeCommand(GCONF_CMD, args, returnOutput=True) if result == 0: self.origMediaAutomount = output[0][:-1] # pylint: disable=W0201 if self.origMediaAutomount == "true": args = [ "--type", "bool", "--set", "/apps/nautilus/preferences/media_automount", "false", ] executeCommand(GCONF_CMD, args) args = [ "--get", "/apps/nautilus/preferences/media_automount_open", ] (result, output) = executeCommand(GCONF_CMD, args, returnOutput=True) if result == 0: self.origMediaAutomountOpen = output[0][:-1] # pylint: disable=W0201 if self.origMediaAutomountOpen == "true": args = [ "--type", "bool", "--set", "/apps/nautilus/preferences/media_automount_open", "false", ] executeCommand(GCONF_CMD, args) def enableGnomeAutomount(self): """ Resets GNOME auto-mounting options back to their state prior to disableGnomeAutomount(). """ if self.origMediaAutomount == "true": args = [ "--type", "bool", "--set", "/apps/nautilus/preferences/media_automount", "true", ] executeCommand(GCONF_CMD, args) if self.origMediaAutomountOpen == "true": args = [ "--type", "bool", "--set", "/apps/nautilus/preferences/media_automount_open", "true", ] executeCommand(GCONF_CMD, args) ################### # Test constructor ################### def testConstructor_001(self): """ Test the constructor using all default arguments. """ isoImage = IsoImage() self.assertEqual(None, isoImage.device) self.assertEqual(None, isoImage.boundaries) self.assertEqual(None, isoImage.graftPoint) self.assertEqual(True, isoImage.useRockRidge) self.assertEqual(None, isoImage.applicationId) self.assertEqual(None, isoImage.biblioFile) self.assertEqual(None, isoImage.publisherId) self.assertEqual(None, isoImage.preparerId) self.assertEqual(None, isoImage.volumeId) def testConstructor_002(self): """ Test the constructor using non-default arguments. """ isoImage = IsoImage("/dev/cdrw", boundaries=(1, 2), graftPoint="/france") self.assertEqual("/dev/cdrw", isoImage.device) self.assertEqual((1, 2), isoImage.boundaries) self.assertEqual("/france", isoImage.graftPoint) self.assertEqual(True, isoImage.useRockRidge) self.assertEqual(None, isoImage.applicationId) self.assertEqual(None, isoImage.biblioFile) self.assertEqual(None, isoImage.publisherId) self.assertEqual(None, isoImage.preparerId) self.assertEqual(None, isoImage.volumeId) ################################ # Test IsoImage utility methods ################################ def testUtilityMethods_001(self): """ Test _buildDirEntries() with an empty entries dictionary. """ entries = {} result = IsoImage._buildDirEntries(entries) self.assertEqual(0, len(result)) def testUtilityMethods_002(self): """ Test _buildDirEntries() with an entries dictionary that has no graft points. """ entries = {} entries["/one/two/three"] = None entries["/four/five/six"] = None entries["/seven/eight/nine"] = None result = IsoImage._buildDirEntries(entries) self.assertEqual(3, len(result)) self.assertTrue("/one/two/three" in result) self.assertTrue("/four/five/six" in result) self.assertTrue("/seven/eight/nine" in result) def testUtilityMethods_003(self): """ Test _buildDirEntries() with an entries dictionary that has all graft points. """ entries = {} entries["/one/two/three"] = "/backup1" entries["/four/five/six"] = "backup2" entries["/seven/eight/nine"] = "backup3" result = IsoImage._buildDirEntries(entries) self.assertEqual(3, len(result)) self.assertTrue("backup1/=/one/two/three" in result) self.assertTrue("backup2/=/four/five/six" in result) self.assertTrue("backup3/=/seven/eight/nine" in result) def testUtilityMethods_004(self): """ Test _buildDirEntries() with an entries dictionary that has mixed graft points and not. """ entries = {} entries["/one/two/three"] = "backup1" entries["/four/five/six"] = None entries["/seven/eight/nine"] = "/backup3" result = IsoImage._buildDirEntries(entries) self.assertEqual(3, len(result)) self.assertTrue("backup1/=/one/two/three" in result) self.assertTrue("/four/five/six" in result) self.assertTrue("backup3/=/seven/eight/nine" in result) def testUtilityMethods_005(self): """ Test _buildGeneralArgs() with all optional values as None. """ isoImage = IsoImage() result = isoImage._buildGeneralArgs() self.assertEqual(0, len(result)) def testUtilityMethods_006(self): """ Test _buildGeneralArgs() with applicationId set. """ isoImage = IsoImage() isoImage.applicationId = "one" result = isoImage._buildGeneralArgs() self.assertEqual(["-A", "one"], result) def testUtilityMethods_007(self): """ Test _buildGeneralArgs() with biblioFile set. """ isoImage = IsoImage() isoImage.biblioFile = "two" result = isoImage._buildGeneralArgs() self.assertEqual(["-biblio", "two"], result) def testUtilityMethods_008(self): """ Test _buildGeneralArgs() with publisherId set. """ isoImage = IsoImage() isoImage.publisherId = "three" result = isoImage._buildGeneralArgs() self.assertEqual(["-publisher", "three"], result) def testUtilityMethods_009(self): """ Test _buildGeneralArgs() with preparerId set. """ isoImage = IsoImage() isoImage.preparerId = "four" result = isoImage._buildGeneralArgs() self.assertEqual(["-p", "four"], result) def testUtilityMethods_010(self): """ Test _buildGeneralArgs() with volumeId set. """ isoImage = IsoImage() isoImage.volumeId = "five" result = isoImage._buildGeneralArgs() self.assertEqual(["-V", "five"], result) def testUtilityMethods_011(self): """ Test _buildSizeArgs() with device and boundaries at defaults. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage() result = isoImage._buildSizeArgs(entries) self.assertEqual(["-print-size", "-graft-points", "-r", "backup1/=/one/two/three"], result) def testUtilityMethods_012(self): """ Test _buildSizeArgs() with useRockRidge set to True and device and boundaries at defaults. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage() isoImage.useRockRidge = True result = isoImage._buildSizeArgs(entries) self.assertEqual(["-print-size", "-graft-points", "-r", "backup1/=/one/two/three"], result) def testUtilityMethods_013(self): """ Test _buildSizeArgs() with useRockRidge set to False and device and boundaries at defaults. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage() isoImage.useRockRidge = False result = isoImage._buildSizeArgs(entries) self.assertEqual(["-print-size", "-graft-points", "backup1/=/one/two/three"], result) def testUtilityMethods_014(self): """ Test _buildSizeArgs() with device as None and boundaries as non-None. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage(device=None, boundaries=(1, 2)) result = isoImage._buildSizeArgs(entries) self.assertEqual(["-print-size", "-graft-points", "-r", "backup1/=/one/two/three"], result) def testUtilityMethods_015(self): """ Test _buildSizeArgs() with device as non-None and boundaries as None. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage(device="/dev/cdrw", boundaries=None) result = isoImage._buildSizeArgs(entries) self.assertEqual(["-print-size", "-graft-points", "-r", "backup1/=/one/two/three"], result) def testUtilityMethods_016(self): """ Test _buildSizeArgs() with device and boundaries as non-None. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage(device="/dev/cdrw", boundaries=(1, 2)) result = isoImage._buildSizeArgs(entries) self.assertEqual(["-print-size", "-graft-points", "-r", "-C", "1,2", "-M", "/dev/cdrw", "backup1/=/one/two/three"], result) def testUtilityMethods_017(self): """ Test _buildWriteArgs() with device and boundaries at defaults. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage() result = isoImage._buildWriteArgs(entries, "/tmp/file.iso") self.assertEqual(["-graft-points", "-r", "-o", "/tmp/file.iso", "backup1/=/one/two/three"], result) def testUtilityMethods_018(self): """ Test _buildWriteArgs() with useRockRidge set to True and device and boundaries at defaults. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage() isoImage.useRockRidge = True result = isoImage._buildWriteArgs(entries, "/tmp/file.iso") self.assertEqual(["-graft-points", "-r", "-o", "/tmp/file.iso", "backup1/=/one/two/three"], result) def testUtilityMethods_019(self): """ Test _buildWriteArgs() with useRockRidge set to False and device and boundaries at defaults. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage() isoImage.useRockRidge = False result = isoImage._buildWriteArgs(entries, "/tmp/file.iso") self.assertEqual(["-graft-points", "-o", "/tmp/file.iso", "backup1/=/one/two/three"], result) def testUtilityMethods_020(self): """ Test _buildWriteArgs() with device as None and boundaries as non-None. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage(device=None, boundaries=(3, 4)) isoImage.useRockRidge = False result = isoImage._buildWriteArgs(entries, "/tmp/file.iso") self.assertEqual(["-graft-points", "-o", "/tmp/file.iso", "backup1/=/one/two/three"], result) def testUtilityMethods_021(self): """ Test _buildWriteArgs() with device as non-None and boundaries as None. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage(device="/dev/cdrw", boundaries=None) isoImage.useRockRidge = False result = isoImage._buildWriteArgs(entries, "/tmp/file.iso") self.assertEqual(["-graft-points", "-o", "/tmp/file.iso", "backup1/=/one/two/three"], result) def testUtilityMethods_022(self): """ Test _buildWriteArgs() with device and boundaries as non-None. """ entries = {} entries["/one/two/three"] = "backup1" isoImage = IsoImage(device="/dev/cdrw", boundaries=(3, 4)) isoImage.useRockRidge = False result = isoImage._buildWriteArgs(entries, "/tmp/file.iso") self.assertEqual( ["-graft-points", "-o", "/tmp/file.iso", "-C", "3,4", "-M", "/dev/cdrw", "backup1/=/one/two/three"], result ) ################## # Test addEntry() ################## def testAddEntry_001(self): """ Attempt to add a non-existent entry. """ file1 = self.buildPath([INVALID_FILE]) isoImage = IsoImage() self.assertRaises(ValueError, isoImage.addEntry, file1) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddEntry_002(self): """ Attempt to add a an entry that is a soft link to a file. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "dir002", "link003"]) isoImage = IsoImage() self.assertRaises(ValueError, isoImage.addEntry, file1) @unittest.skipUnless(platformSupportsLinks(), "Requires soft links") def testAddEntry_003(self): """ Attempt to add a an entry that is a soft link to a directory """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "link001"]) isoImage = IsoImage() self.assertRaises(ValueError, isoImage.addEntry, file1) def testAddEntry_004(self): """ Attempt to add a file, no graft point set. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1) self.assertEqual({file1: None}, isoImage.entries) def testAddEntry_005(self): """ Attempt to add a file, graft point set on the object level. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage(graftPoint="whatever") self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1) self.assertEqual({file1: "whatever"}, isoImage.entries) def testAddEntry_006(self): """ Attempt to add a file, graft point set on the method level. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, graftPoint="stuff") self.assertEqual({file1: "stuff"}, isoImage.entries) def testAddEntry_007(self): """ Attempt to add a file, graft point set on the object and method levels. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage(graftPoint="whatever") self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, graftPoint="stuff") self.assertEqual({file1: "stuff"}, isoImage.entries) def testAddEntry_008(self): """ Attempt to add a file, graft point set on the object and method levels, where method value is None (which can't be distinguished from the method value being unset). """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage(graftPoint="whatever") self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, graftPoint=None) self.assertEqual({file1: "whatever"}, isoImage.entries) def testAddEntry_009(self): """ Attempt to add a directory, no graft point set. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(dir1) self.assertEqual({dir1: os.path.basename(dir1)}, isoImage.entries) def testAddEntry_010(self): """ Attempt to add a directory, graft point set on the object level. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage(graftPoint="p") self.assertEqual({}, isoImage.entries) isoImage.addEntry(dir1) self.assertEqual({dir1: pathJoin("p", "tree9")}, isoImage.entries) def testAddEntry_011(self): """ Attempt to add a directory, graft point set on the method level. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(dir1, graftPoint="s") self.assertEqual({dir1: pathJoin("s", "tree9")}, isoImage.entries) def testAddEntry_012(self): """ Attempt to add a file, no graft point set, contentsOnly=True. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, contentsOnly=True) self.assertEqual({file1: None}, isoImage.entries) def testAddEntry_013(self): """ Attempt to add a file, graft point set on the object level, contentsOnly=True. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage(graftPoint="whatever") self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, contentsOnly=True) self.assertEqual({file1: "whatever"}, isoImage.entries) def testAddEntry_014(self): """ Attempt to add a file, graft point set on the method level, contentsOnly=True. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, graftPoint="stuff", contentsOnly=True) self.assertEqual({file1: "stuff"}, isoImage.entries) def testAddEntry_015(self): """ Attempt to add a file, graft point set on the object and method levels, contentsOnly=True. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage(graftPoint="whatever") self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, graftPoint="stuff", contentsOnly=True) self.assertEqual({file1: "stuff"}, isoImage.entries) def testAddEntry_016(self): """ Attempt to add a file, graft point set on the object and method levels, where method value is None (which can't be distinguished from the method value being unset), contentsOnly=True. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage(graftPoint="whatever") self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, graftPoint=None, contentsOnly=True) self.assertEqual({file1: "whatever"}, isoImage.entries) def testAddEntry_017(self): """ Attempt to add a directory, no graft point set, contentsOnly=True. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(dir1, contentsOnly=True) self.assertEqual({dir1: None}, isoImage.entries) def testAddEntry_018(self): """ Attempt to add a directory, graft point set on the object level, contentsOnly=True. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage(graftPoint="p") self.assertEqual({}, isoImage.entries) isoImage.addEntry(dir1, contentsOnly=True) self.assertEqual({dir1: "p"}, isoImage.entries) def testAddEntry_019(self): """ Attempt to add a directory, graft point set on the method level, contentsOnly=True. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(dir1, graftPoint="s", contentsOnly=True) self.assertEqual({dir1: "s"}, isoImage.entries) def testAddEntry_020(self): """ Attempt to add a directory, graft point set on the object and methods levels, contentsOnly=True. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage(graftPoint="p") self.assertEqual({}, isoImage.entries) isoImage.addEntry(dir1, graftPoint="s", contentsOnly=True) self.assertEqual({dir1: "s"}, isoImage.entries) def testAddEntry_021(self): """ Attempt to add a directory, graft point set on the object and methods levels, contentsOnly=True. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage(graftPoint="p") self.assertEqual({}, isoImage.entries) isoImage.addEntry(dir1, graftPoint="s", contentsOnly=True) self.assertEqual({dir1: "s"}, isoImage.entries) def testAddEntry_022(self): """ Attempt to add a file that has already been added, override=False. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1) self.assertEqual({file1: None}, isoImage.entries) self.assertRaises(ValueError, isoImage.addEntry, file1, override=False) self.assertEqual({file1: None}, isoImage.entries) def testAddEntry_023(self): """ Attempt to add a file that has already been added, override=True. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage() self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1) self.assertEqual({file1: None}, isoImage.entries) isoImage.addEntry(file1, override=True) self.assertEqual({file1: None}, isoImage.entries) def testAddEntry_024(self): """ Attempt to add a directory that has already been added, override=False, changing the graft point. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage(graftPoint="whatever") self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, graftPoint="one") self.assertEqual({file1: "one"}, isoImage.entries) self.assertRaises(ValueError, isoImage.addEntry, file1, graftPoint="two", override=False) self.assertEqual({file1: "one"}, isoImage.entries) def testAddEntry_025(self): """ Attempt to add a directory that has already been added, override=True, changing the graft point. """ self.extractTar("tree9") file1 = self.buildPath(["tree9", "file001"]) isoImage = IsoImage(graftPoint="whatever") self.assertEqual({}, isoImage.entries) isoImage.addEntry(file1, graftPoint="one") self.assertEqual({file1: "one"}, isoImage.entries) isoImage.addEntry(file1, graftPoint="two", override=True) self.assertEqual({file1: "two"}, isoImage.entries) ########################## # Test getEstimatedSize() ########################## @unittest.skipUnless(runAllTests(), "Limited test suite") def testGetEstimatedSize_001(self): """ Test with an empty list. """ self.extractTar("tree9") isoImage = IsoImage() self.assertRaises(ValueError, isoImage.getEstimatedSize) @unittest.skipUnless(runAllTests(), "Limited test suite") def testGetEstimatedSize_002(self): """ Test with non-empty empty list. """ self.extractTar("tree9") dir1 = self.buildPath(["tree9"]) isoImage = IsoImage() isoImage.addEntry(dir1, graftPoint="base") result = isoImage.getEstimatedSize() self.assertTrue(result > 0) #################### # Test writeImage() #################### @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_001(self): """ Attempt to write an image containing no entries. """ isoImage = IsoImage() imagePath = self.buildPath(["image.iso"]) self.assertRaises(ValueError, isoImage.writeImage, imagePath) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_002(self): """ Attempt to write an image containing only an empty directory, no graft point. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9", "dir001", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(2, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_003(self): """ Attempt to write an image containing only an empty directory, with a graft point. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9", "dir001", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1, graftPoint="base") isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(3, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "base") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_004(self): """ Attempt to write an image containing only a non-empty directory, no graft point. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(10, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "dir002") in fsList) self.assertTrue(pathJoin(mountPath, "dir002", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "dir002", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "dir002", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "dir002", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "dir002", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "dir002", "link004") in fsList) self.assertTrue(pathJoin(mountPath, "dir002", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "dir002", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_005(self): """ Attempt to write an image containing only a non-empty directory, with a graft point. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1, graftPoint=pathJoin("something", "else")) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(12, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "something") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002", "link004") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_006(self): """ Attempt to write an image containing only a file, no graft point. """ self.extractTar("tree9") isoImage = IsoImage() file1 = self.buildPath(["tree9", "file001"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(2, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "file001") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_007(self): """ Attempt to write an image containing only a file, with a graft point. """ self.extractTar("tree9") isoImage = IsoImage() file1 = self.buildPath(["tree9", "file001"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, graftPoint="point") isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(3, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "point") in fsList) self.assertTrue(pathJoin(mountPath, "point", "file001") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_008(self): """ Attempt to write an image containing a file and an empty directory, no graft points. """ self.extractTar("tree9") isoImage = IsoImage() file1 = self.buildPath(["tree9", "file001"]) dir1 = self.buildPath(["tree9", "dir001", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1) isoImage.addEntry(dir1) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(3, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "file001") in fsList) self.assertTrue(pathJoin(mountPath, "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_009(self): """ Attempt to write an image containing a file and an empty directory, with graft points. """ self.extractTar("tree9") isoImage = IsoImage(graftPoint="base") file1 = self.buildPath(["tree9", "file001"]) dir1 = self.buildPath(["tree9", "dir001", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, graftPoint="other") isoImage.addEntry(dir1) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(5, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "other") in fsList) self.assertTrue(pathJoin(mountPath, "base") in fsList) self.assertTrue(pathJoin(mountPath, "other", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_010(self): """ Attempt to write an image containing a file and a non-empty directory, mixed graft points. """ self.extractTar("tree9") isoImage = IsoImage(graftPoint="base") file1 = self.buildPath(["tree9", "file001"]) dir1 = self.buildPath(["tree9", "dir001"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, graftPoint=None) isoImage.addEntry(dir1) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(11, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "base") in fsList) self.assertTrue(pathJoin(mountPath, "base", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_011(self): """ Attempt to write an image containing several files and a non-empty directory, mixed graft points. """ self.extractTar("tree9") isoImage = IsoImage() file1 = self.buildPath(["tree9", "file001"]) file2 = self.buildPath(["tree9", "file002"]) dir1 = self.buildPath(["tree9", "dir001"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1) isoImage.addEntry(file2, graftPoint="other") isoImage.addEntry(dir1, graftPoint="base") isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(13, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "base") in fsList) self.assertTrue(pathJoin(mountPath, "other") in fsList) self.assertTrue(pathJoin(mountPath, "file001") in fsList) self.assertTrue(pathJoin(mountPath, "other", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_012(self): """ Attempt to write an image containing a deeply-nested directory. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1, graftPoint="something") isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(24, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "something") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir001", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir001", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir001", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir001", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir001", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir001", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir001", "dir002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002", "link004") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "tree9", "dir002", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_013(self): """ Attempt to write an image containing only an empty directory, no graft point, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9", "dir001", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1, contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(1, len(fsList)) self.assertTrue(mountPath in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_014(self): """ Attempt to write an image containing only an empty directory, with a graft point, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9", "dir001", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1, graftPoint="base", contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(2, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "base") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_015(self): """ Attempt to write an image containing only a non-empty directory, no graft point, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1, contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(9, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "file001") in fsList) self.assertTrue(pathJoin(mountPath, "file002") in fsList) self.assertTrue(pathJoin(mountPath, "link001") in fsList) self.assertTrue(pathJoin(mountPath, "link002") in fsList) self.assertTrue(pathJoin(mountPath, "link003") in fsList) self.assertTrue(pathJoin(mountPath, "link004") in fsList) self.assertTrue(pathJoin(mountPath, "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_016(self): """ Attempt to write an image containing only a non-empty directory, with a graft point, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1, graftPoint=pathJoin("something", "else"), contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(11, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "something") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "link004") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "else", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_017(self): """ Attempt to write an image containing only a file, no graft point, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() file1 = self.buildPath(["tree9", "file001"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(2, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "file001") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_018(self): """ Attempt to write an image containing only a file, with a graft point, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() file1 = self.buildPath(["tree9", "file001"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, graftPoint="point", contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(3, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "point") in fsList) self.assertTrue(pathJoin(mountPath, "point", "file001") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_019(self): """ Attempt to write an image containing a file and an empty directory, no graft points, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() file1 = self.buildPath(["tree9", "file001"]) dir1 = self.buildPath(["tree9", "dir001", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, contentsOnly=True) isoImage.addEntry(dir1, contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(2, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "file001") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_020(self): """ Attempt to write an image containing a file and an empty directory, with graft points, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage(graftPoint="base") file1 = self.buildPath(["tree9", "file001"]) dir1 = self.buildPath(["tree9", "dir001", "dir002"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, graftPoint="other", contentsOnly=True) isoImage.addEntry(dir1, contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(4, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "other") in fsList) self.assertTrue(pathJoin(mountPath, "base") in fsList) self.assertTrue(pathJoin(mountPath, "other", "file001") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_021(self): """ Attempt to write an image containing a file and a non-empty directory, mixed graft points, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage(graftPoint="base") file1 = self.buildPath(["tree9", "file001"]) dir1 = self.buildPath(["tree9", "dir001"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, graftPoint=None, contentsOnly=True) isoImage.addEntry(dir1, contentsOnly=True) self.assertRaises(IOError, isoImage.writeImage, imagePath) # ends up with a duplicate name @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_022(self): """ Attempt to write an image containing several files and a non-empty directory, mixed graft points, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() file1 = self.buildPath(["tree9", "file001"]) file2 = self.buildPath(["tree9", "file002"]) dir1 = self.buildPath(["tree9", "dir001"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(file1, contentsOnly=True) isoImage.addEntry(file2, graftPoint="other", contentsOnly=True) isoImage.addEntry(dir1, graftPoint="base", contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(12, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "base") in fsList) self.assertTrue(pathJoin(mountPath, "other") in fsList) self.assertTrue(pathJoin(mountPath, "file001") in fsList) self.assertTrue(pathJoin(mountPath, "other", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "base", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "base", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "base", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "base", "dir002") in fsList) @unittest.skipUnless(runAllTests(), "Limited test suite") def testWriteImage_023(self): """ Attempt to write an image containing a deeply-nested directory, contentsOnly=True. """ self.extractTar("tree9") isoImage = IsoImage() dir1 = self.buildPath(["tree9"]) imagePath = self.buildPath(["image.iso"]) isoImage.addEntry(dir1, graftPoint="something", contentsOnly=True) isoImage.writeImage(imagePath) mountPath = self.mountImage(imagePath) fsList = FilesystemList() fsList.addDirContents(mountPath) self.assertEqual(23, len(fsList)) self.assertTrue(mountPath in fsList) self.assertTrue(pathJoin(mountPath, "something") in fsList) self.assertTrue(pathJoin(mountPath, "something", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir001", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir001", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir001", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir001", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir001", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir001", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir001", "dir002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002", "file001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002", "file002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002", "link001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002", "link002") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002", "link003") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002", "link004") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002", "dir001") in fsList) self.assertTrue(pathJoin(mountPath, "something", "dir002", "dir002") in fsList) cedar_backup3-3.8.1/PKG-INFO0000644000000000000000000001017300000000000012177 0ustar00Metadata-Version: 2.1 Name: cedar-backup3 Version: 3.8.1 Summary: Implements local and remote backups to CD/DVD and Amazon S3 Home-page: https://pypi.org/project/cedar-backup3/ License: GPL-2.0-only Keywords: local,remote,backup,scp Author: Kenneth J. Pronovici Author-email: pronovic@ieee.org Requires-Python: >=3.9,<4 Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: GNU General Public License v2 (GPLv2) Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: System :: Archiving :: Backup Classifier: Topic :: Utilities Provides-Extra: docs Requires-Dist: astroid (<3) Requires-Dist: chardet (>=5.1.0,<6.0.0) Requires-Dist: importlib-metadata (>=4.12.0,<5.0.0) Requires-Dist: sphinx (>=5.3.0,<6.0.0) ; extra == "docs" Requires-Dist: sphinx-autoapi (>=2.0.0,<3.0.0) ; extra == "docs" Project-URL: Repository, https://github.com/pronovic/cedar-backup3 Description-Content-Type: text/markdown [![pypi](https://img.shields.io/pypi/v/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![license](https://img.shields.io/pypi/l/cedar-backup3.svg)](https://github.com/pronovic/cedar-backup3/blob/master/LICENSE) [![wheel](https://img.shields.io/pypi/wheel/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![python](https://img.shields.io/pypi/pyversions/cedar-backup3.svg)](https://pypi.org/project/cedar-backup3/) [![Test Suite](https://github.com/pronovic/cedar-backup3/workflows/Test%20Suite/badge.svg)](https://github.com/pronovic/cedar-backup3/actions?query=workflow%3A%22Test+Suite%22) [![docs](https://readthedocs.org/projects/cedar-backup3/badge/?version=stable&style=flat)](https://cedar-backup3.readthedocs.io/en/stable/) [![coverage](https://coveralls.io/repos/github/pronovic/cedar-backup3/badge.svg?branch=master)](https://coveralls.io/github/pronovic/cedar-backup3?branch=master) [![Poetry](https://img.shields.io/endpoint?url=https://python-poetry.org/badge/v0.json)](https://python-poetry.org/) [Cedar Backup](https://github.com/pronovic/cedar-backup3) is a software package designed to manage system backups for a pool of local and remote machines. Cedar Backup understands how to back up filesystem data as well as MySQL and PostgreSQL databases and Subversion repositories. It can also be easily extended to support other kinds of data sources. Cedar Backup is focused around weekly backups to a single CD or DVD disc, with the expectation that the disc will be changed or overwritten at the beginning of each week. If your hardware is new enough, Cedar Backup can write multisession discs, allowing you to add incremental data to a disc on a daily basis. Alternately, Cedar Backup can write your backups to the Amazon S3 cloud rather than relying on physical media. See the [Cedar Backup v3 Software Manual](https://cedar-backup3.readthedocs.io/en/stable/manual/index.html) for details. Besides offering command-line utilities to manage the backup process, Cedar Backup provides a well-organized library of backup-related functionality. For more information, see the [API Reference](https://cedar-backup3.readthedocs.io/en/stable/autoapi/index.html). There are many different backup software systems in the open source world. Cedar Backup aims to fill a niche: it aims to be a good fit for people who need to back up a limited amount of important data on a regular basis. Cedar Backup isn’t for you if you want to back up your huge MP3 collection every night, or if you want to back up a few hundred machines. However, if you administer a small set of machines and you want to run daily incremental backups for things like system configuration, current email, small web sites, source code repositories, or small databases, then Cedar Backup is probably worth your time.