From 90da7f3c0f4e92a1c44871de038e801d764ccc93 Mon Sep 17 00:00:00 2001 From: Scott Date: Sun, 6 Sep 2015 21:27:05 -0400 Subject: [PATCH] If we're rocking PHP 7, this library should be a NOP --- ERRATA.md | 12 +- lib/error_polyfill.php | 8 + lib/random.php | 88 +- tests/phpunit.phar | 72774 +++++++++++++++++++++++++++++++ tests/phpunit.phar.asc | 17 + tests/unit/RandomBytesTest.php | 5 +- 6 files changed, 72852 insertions(+), 52 deletions(-) create mode 100644 tests/phpunit.phar create mode 100644 tests/phpunit.phar.asc diff --git a/ERRATA.md b/ERRATA.md index f73cd3c..66fb856 100644 --- a/ERRATA.md +++ b/ERRATA.md @@ -9,9 +9,8 @@ The order is: 3. `COM('CAPICOM.Utilities.1')->GetRandom()` 4. `openssl_random_pseudo_bytes()` -We read `/dev/urandom` first (if it exists). -This is the preferred file to read for random data for cryptographic -purposes for BSD and Linux. +We read `/dev/urandom` first (if it exists). This is the preferred file to read +for random data for cryptographic purposes for BSD and Linux. Despite [strongly urging people not to use mcrypt in their projects](https://paragonie.com/blog/2015/05/if-you-re-typing-word-mcrypt-into-your-code-you-re-doing-it-wrong), because libmcrypt is abandonware and the API puts too much responsibility on the @@ -28,8 +27,9 @@ and is not part `libmcrypt`. It actually does the right thing: If we're on Windows and don't have access to `mcrypt`, we use `CAPICOM.Utilities.1`. -Finally, we use `openssl_random_pseudo_bytes()` **as a last resort**. Internally, -this function calls `RAND_pseudo_bytes()`, which has been [deprecated](https://github.com/paragonie/random_compat/issues/5) +Finally, we use `openssl_random_pseudo_bytes()` **as a last resort**, due to +[PHP bug #70014](https://bugs.php.net/bug.php?id=70014). Internally, this +function calls `RAND_pseudo_bytes()`, which has been [deprecated](https://github.com/paragonie/random_compat/issues/5) by the OpenSSL team. Furthermore, [it might silently return weak random data](https://github.com/paragonie/random_compat/issues/6#issuecomment-119564973) if it is called before OpenSSL's **userspace** CSPRNG is seeded. Also, -[you want the OS CSPRNG, not a userspace CSPRNG](http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/). +[you want the OS CSPRNG, not a userspace CSPRNG](http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/). \ No newline at end of file diff --git a/lib/error_polyfill.php b/lib/error_polyfill.php index bfc73e1..7253ff7 100644 --- a/lib/error_polyfill.php +++ b/lib/error_polyfill.php @@ -30,10 +30,18 @@ interface Throwable { public function getMessage(); + public function getCode(); + public function getFile(); + public function getLine(); + public function getTrace(); + public function getTraceAsString(); + public function getPrevious(); + public function __toString(); } } if (!class_exists('Error', false)) { + // We can't really avoid making this extend Exception in PHP 5. class Error extends Exception implements Throwable { diff --git a/lib/random.php b/lib/random.php index 718abcb..ca72197 100644 --- a/lib/random.php +++ b/lib/random.php @@ -26,55 +26,55 @@ * SOFTWARE. */ -if (!defined('RANDOM_COMPAT_READ_BUFFER')) { - define('RANDOM_COMPAT_READ_BUFFER', 8); -} if (!defined('PHP_VERSION_ID')) { + // This constant was introduced in PHP 5.2.7 $version = explode('.', PHP_VERSION); define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2])); } - -require_once "error_polyfill.php"; -require_once "byte_safe_strings.php"; - -if (!function_exists('random_bytes')) { - /** - * PHP 5.2.0 - 5.6.x way to implement random_bytes() - * - * We use conditional statements here to define the function in accordance - * to the operating environment. It's a micro-optimization. - * - * In order of preference: - * 1. fread() /dev/urandom if available - * 2. mcrypt_create_iv($bytes, MCRYPT_CREATE_IV) - * 3. COM('CAPICOM.Utilities.1')->GetRandom() - * 4. openssl_random_pseudo_bytes() - * - * See ERRATA.md for our reasoning behind this particular order - */ - if (!ini_get('open_basedir') && is_readable('/dev/urandom')) { - // See random_bytes_dev_urandom.php - require_once "random_bytes_dev_urandom.php"; - } elseif (PHP_VERSION_ID >= 50307 && function_exists('mcrypt_create_iv')) { - // See random_bytes_mcrypt.php - require_once "random_bytes_mcrypt.php"; - } elseif (extension_loaded('com_dotnet')) { - // See random_bytes_com_dotnet.php - require_once "random_bytes_com_dotnet.php"; - } elseif (function_exists('openssl_random_pseudo_bytes')) { - // See random_bytes_openssl.php - require_once "random_bytes_openssl.php"; - } else { +if (PHP_VERSION_ID < 70000) { + if (!defined('RANDOM_COMPAT_READ_BUFFER')) { + define('RANDOM_COMPAT_READ_BUFFER', 8); + } + require_once "byte_safe_strings.php"; + require_once "error_polyfill.php"; + if (!function_exists('random_bytes')) { /** - * We don't have any more options, so let's throw an exception right now - * and hope the developer won't let it fail silently. + * PHP 5.2.0 - 5.6.x way to implement random_bytes() + * + * We use conditional statements here to define the function in accordance + * to the operating environment. It's a micro-optimization. + * + * In order of preference: + * 1. fread() /dev/urandom if available + * 2. mcrypt_create_iv($bytes, MCRYPT_CREATE_IV) + * 3. COM('CAPICOM.Utilities.1')->GetRandom() + * 4. openssl_random_pseudo_bytes() + * + * See ERRATA.md for our reasoning behind this particular order */ - throw new Exception( - 'There is no suitable CSPRNG installed on your system' - ); + if (!ini_get('open_basedir') && is_readable('/dev/urandom')) { + // See random_bytes_dev_urandom.php + require_once "random_bytes_dev_urandom.php"; + } elseif (PHP_VERSION_ID >= 50307 && function_exists('mcrypt_create_iv')) { + // See random_bytes_mcrypt.php + require_once "random_bytes_mcrypt.php"; + } elseif (extension_loaded('com_dotnet')) { + // See random_bytes_com_dotnet.php + require_once "random_bytes_com_dotnet.php"; + } elseif (function_exists('openssl_random_pseudo_bytes')) { + // See random_bytes_openssl.php + require_once "random_bytes_openssl.php"; + } else { + /** + * We don't have any more options, so let's throw an exception right now + * and hope the developer won't let it fail silently. + */ + throw new Exception( + 'There is no suitable CSPRNG installed on your system' + ); + } + } + if (!function_exists('random_int')) { + require_once "random_int.php"; } } -if (!function_exists('random_int')) { - require_once "random_int.php"; -} - diff --git a/tests/phpunit.phar b/tests/phpunit.phar new file mode 100644 index 0000000..8e4c740 --- /dev/null +++ b/tests/phpunit.phar @@ -0,0 +1,72774 @@ +#!/usr/bin/env php +')) { + fwrite( + STDERR, + 'This version of PHPUnit requires PHP 5.3.3; using the latest version of PHP is highly recommended.' . PHP_EOL + ); + + die(1); + } + + if (isset($_SERVER['argv'][1]) && $_SERVER['argv'][1] == '--manifest') { + print file_get_contents(__PHPUNIT_PHAR_ROOT__ . '/phar/manifest.txt'); + exit; + } + + PHPUnit_TextUI_Command::main(); +} + +__HALT_COMPILER(); ?> +:phpunit-4.8.6.phar)phar/sebastian-global-state/Exception.phpU (phar/sebastian-global-state/Snapshot.php',U',5+ #phar/sebastian-global-state/LICENSE +U +(phar/sebastian-global-state/Restorer.phpU}FԶ)phar/sebastian-global-state/Blacklist.php#U#f10phar/sebastian-global-state/RuntimeException.php0 U0 yvphar/php-text-template/LICENSE U S:#phar/php-text-template/Template.php U w4phar/sebastian-diff/LICENSEUvEvö4phar/sebastian-diff/LCS/LongestCommonSubsequence.phpU؛Qphar/sebastian-diff/LCS/MemoryEfficientLongestCommonSubsequenceImplementation.php +U +g8Ophar/sebastian-diff/LCS/TimeEfficientLongestCommonSubsequenceImplementation.phpUrphar/sebastian-diff/Line.phpUζphar/sebastian-diff/Differ.phpU<6phar/sebastian-diff/Parser.phps Us v ^phar/sebastian-diff/Diff.phpUphar/sebastian-diff/Chunk.phpU9LKphar/php-timer/Timer.phpE UE *Ephar/php-timer/LICENSEUǨAE!phar/sebastian-comparator/LICENSE U :(phar/sebastian-comparator/Comparator.phpPUP!P=8phar/sebastian-comparator/SplObjectStorageComparator.php +U +MQ,phar/sebastian-comparator/TypeComparator.phpUQ.phar/sebastian-comparator/ObjectComparator.phpUw0phar/sebastian-comparator/ResourceComparator.phpU&w-phar/sebastian-comparator/ArrayComparator.phpUvx0phar/sebastian-comparator/DateTimeComparator.php U 1phar/sebastian-comparator/ExceptionComparator.phpUkf/phar/sebastian-comparator/DOMNodeComparator.php U >%.phar/sebastian-comparator/ScalarComparator.php>U>B(2phar/sebastian-comparator/MockObjectComparator.phpUO/phar/sebastian-comparator/NumericComparator.php~ +U~ +7%phar/sebastian-comparator/Factory.phpd Ud ه1.phar/sebastian-comparator/DoubleComparator.php7U7Stٶ/phar/sebastian-comparator/ComparisonFailure.php U Vphar/phpspec-prophecy/LICENSE}U}6=phar/phpspec-prophecy/Prophecy/Prophecy/RevealerInterface.phpHUHgZDphar/phpspec-prophecy/Prophecy/Prophecy/ProphecySubjectInterface.phpUi4phar/phpspec-prophecy/Prophecy/Prophecy/Revealer.phpUjɸ=phar/phpspec-prophecy/Prophecy/Prophecy/ProphecyInterface.php,U,W:phar/phpspec-prophecy/Prophecy/Prophecy/ObjectProphecy.phpkUkkʶ:phar/phpspec-prophecy/Prophecy/Prophecy/MethodProphecy.php_,U_,K?phar/phpspec-prophecy/Prophecy/Comparator/ClosureComparator.phpKUK)RQ5phar/phpspec-prophecy/Prophecy/Comparator/Factory.phpUA϶+phar/phpspec-prophecy/Prophecy/Argument.php^U^l*phar/phpspec-prophecy/Prophecy/Prophet.phpUvq6phar/phpspec-prophecy/Prophecy/Exception/Exception.php+U+Mphar/phpspec-prophecy/Prophecy/Exception/Prophecy/ObjectProphecyException.phpU:FGphar/phpspec-prophecy/Prophecy/Exception/Prophecy/ProphecyException.phpU$϶Mphar/phpspec-prophecy/Prophecy/Exception/Prophecy/MethodProphecyException.php)U)F4Ophar/phpspec-prophecy/Prophecy/Exception/Doubler/InterfaceNotFoundException.phpUIphar/phpspec-prophecy/Prophecy/Exception/Doubler/ClassMirrorException.phpUۉ?Dphar/phpspec-prophecy/Prophecy/Exception/Doubler/DoubleException.phpUzFEphar/phpspec-prophecy/Prophecy/Exception/Doubler/DoublerException.phpUZ^Lphar/phpspec-prophecy/Prophecy/Exception/Doubler/MethodNotFoundException.phpUihOphar/phpspec-prophecy/Prophecy/Exception/Doubler/ReturnByReferenceException.phpUJphar/phpspec-prophecy/Prophecy/Exception/Doubler/ClassCreatorException.phpU77/%Kphar/phpspec-prophecy/Prophecy/Exception/Doubler/ClassNotFoundException.phpUh+Pphar/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsException.php,U,aKphar/phpspec-prophecy/Prophecy/Exception/Prediction/PredictionException.phpU2TѶQphar/phpspec-prophecy/Prophecy/Exception/Prediction/FailedPredictionException.phpJUJ~DHphar/phpspec-prophecy/Prophecy/Exception/Prediction/NoCallsException.phpUl<Jphar/phpspec-prophecy/Prophecy/Exception/Prediction/AggregateException.phpU?D<ζUphar/phpspec-prophecy/Prophecy/Exception/Prediction/UnexpectedCallsCountException.phpU ƶIphar/phpspec-prophecy/Prophecy/Exception/Call/UnexpectedCallException.phpUEphar/phpspec-prophecy/Prophecy/Exception/InvalidArgumentException.phpUg8phar/phpspec-prophecy/Prophecy/Doubler/CachedDoubler.phpU̇g:phar/phpspec-prophecy/Prophecy/Doubler/DoubleInterface.phpU8dj2phar/phpspec-prophecy/Prophecy/Doubler/Doubler.phpU8]^Gphar/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCodeGenerator.php +U +`Aphar/phpspec-prophecy/Prophecy/Doubler/Generator/ClassCreator.phpU?BrHphar/phpspec-prophecy/Prophecy/Doubler/Generator/ReflectionInterface.phpU@phar/phpspec-prophecy/Prophecy/Doubler/Generator/ClassMirror.phpU"Fphar/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ArgumentNode.phpU^Dphar/phpspec-prophecy/Prophecy/Doubler/Generator/Node/MethodNode.phpU + Cphar/phpspec-prophecy/Prophecy/Doubler/Generator/Node/ClassNode.phpP +UP +M-5phar/phpspec-prophecy/Prophecy/Doubler/LazyDouble.phpF UF lBphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/KeywordPatch.php U /@ȶJphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ProphecySubjectPatch.php +U +niDphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/MagicCallPatch.phpUyFphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/TraversablePatch.php U jNFphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/SplFileInfoPatch.phpU!h^Mphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/DisableConstructorPatch.phpU:0`Hphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/HhvmExceptionPatch.phpUx^Uphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ReflectionClassNewInstancePatch.phppUpxIphar/phpspec-prophecy/Prophecy/Doubler/ClassPatch/ClassPatchInterface.phplUl)5:8phar/phpspec-prophecy/Prophecy/Doubler/NameGenerator.phpU7Aphar/phpspec-prophecy/Prophecy/Prediction/CallTimesPrediction.php U X?phar/phpspec-prophecy/Prophecy/Prediction/NoCallsPrediction.phpUL9%Aphar/phpspec-prophecy/Prophecy/Prediction/PredictionInterface.phpU`IE@phar/phpspec-prophecy/Prophecy/Prediction/CallbackPrediction.phpUVb{ζ<phar/phpspec-prophecy/Prophecy/Prediction/CallPrediction.phpQ UQ I,phar/phpspec-prophecy/Prophecy/Call/Call.php U {:%2phar/phpspec-prophecy/Prophecy/Call/CallCenter.phpmUm[97phar/phpspec-prophecy/Prophecy/Promise/ThrowPromise.php9 +U9 +cu@phar/phpspec-prophecy/Prophecy/Promise/ReturnArgumentPromise.phpU%A8phar/phpspec-prophecy/Prophecy/Promise/ReturnPromise.phpU؏:phar/phpspec-prophecy/Prophecy/Promise/CallbackPromise.phpU[;phar/phpspec-prophecy/Prophecy/Promise/PromiseInterface.phpKUK=phar/phpspec-prophecy/Prophecy/Argument/ArgumentsWildcard.php4 U4 A;K2?phar/phpspec-prophecy/Prophecy/Argument/Token/AnyValueToken.phpUFhFphar/phpspec-prophecy/Prophecy/Argument/Token/ArrayEveryEntryToken.phpUpbAphar/phpspec-prophecy/Prophecy/Argument/Token/LogicalAndToken.phpU Nv@phar/phpspec-prophecy/Prophecy/Argument/Token/TokenInterface.phpUٰAphar/phpspec-prophecy/Prophecy/Argument/Token/ArrayEntryToken.phpUJ:Aphar/phpspec-prophecy/Prophecy/Argument/Token/ArrayCountToken.phpU4̶@phar/phpspec-prophecy/Prophecy/Argument/Token/AnyValuesToken.phpUbN/Ephar/phpspec-prophecy/Prophecy/Argument/Token/StringContainsToken.phpU>Aphar/phpspec-prophecy/Prophecy/Argument/Token/ExactValueToken.php U 3Bphar/phpspec-prophecy/Prophecy/Argument/Token/ObjectStateToken.php9 +U9 +_Jg?phar/phpspec-prophecy/Prophecy/Argument/Token/CallbackToken.php,U,cR̶Ephar/phpspec-prophecy/Prophecy/Argument/Token/IdenticalValueToken.phpUAphar/phpspec-prophecy/Prophecy/Argument/Token/LogicalNotToken.phpUr;phar/phpspec-prophecy/Prophecy/Argument/Token/TypeToken.phpUn\2phar/phpspec-prophecy/Prophecy/Util/ExportUtil.phpUѪzH2phar/phpspec-prophecy/Prophecy/Util/StringUtil.php U %phar/sebastian-exporter/LICENSEUAe)$phar/sebastian-exporter/Exporter.php["U["Tphar/php-file-iterator/LICENSE U s#phar/php-file-iterator/Iterator.phpaUa!phar/php-file-iterator/Facade.php U 0"phar/php-file-iterator/Factory.php U yphar/manifest.txt_U_nS.phar/sebastian-recursion-context/Exception.phpJUJ(phar/sebastian-recursion-context/LICENSEU,phar/sebastian-recursion-context/Context.phpU ?=phar/sebastian-recursion-context/InvalidArgumentException.phpUmH5phar/phpunit-selenium/Extensions/SeleniumTestCase.phpUvK6phar/phpunit-selenium/Extensions/SeleniumTestSuite.phpU<phar/phpunit-selenium/Extensions/SeleniumTestCase/Driver.phpMUM0R;phar/phpunit-selenium/Extensions/Selenium2TestCase/Keys.phpIUIͶMphar/phpunit-selenium/Extensions/Selenium2TestCase/Session/Cookie/Builder.phpUӸqGphar/phpunit-selenium/Extensions/Selenium2TestCase/Session/Timeouts.php]U]/XFphar/phpunit-selenium/Extensions/Selenium2TestCase/Session/Storage.php8 U8 Ephar/phpunit-selenium/Extensions/Selenium2TestCase/Session/Cookie.phpU$!@phar/phpunit-selenium/Extensions/Selenium2TestCase/Exception.php U H"[Ephar/phpunit-selenium/Extensions/Selenium2TestCase/CommandsHolder.phpUZewIphar/phpunit-selenium/Extensions/Selenium2TestCase/ScreenshotListener.phpUh7Ƕ>phar/phpunit-selenium/Extensions/Selenium2TestCase/Command.phpL UL cyƶCphar/phpunit-selenium/Extensions/Selenium2TestCase/StateCommand.phpw +Uw +e3Z>phar/phpunit-selenium/Extensions/Selenium2TestCase/Element.php U &?phar/phpunit-selenium/Extensions/Selenium2TestCase/Response.phpUTZJphar/phpunit-selenium/Extensions/Selenium2TestCase/NoSeleniumException.php U kMphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionStrategy/Shared.phpHUHVmOphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionStrategy/Isolated.php U ͶKphar/phpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Click.php+ +U+ +ĻLphar/phpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Equals.php` U` nնUphar/phpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/GenericAccessor.phpi +Ui +PeQphar/phpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/GenericPost.phpW +UW +LqKphar/phpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Value.phpG UG +UOphar/phpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Attribute.phpt Ut  ˈIphar/phpunit-selenium/Extensions/Selenium2TestCase/ElementCommand/Css.phpi Ui Fphar/phpunit-selenium/Extensions/Selenium2TestCase/ElementCriteria.phpN UN ¶=phar/phpunit-selenium/Extensions/Selenium2TestCase/Window.phpO UO S=phar/phpunit-selenium/Extensions/Selenium2TestCase/Driver.phpUe5v@phar/phpunit-selenium/Extensions/Selenium2TestCase/WaitUntil.phpUzIphar/phpunit-selenium/Extensions/Selenium2TestCase/WebDriverException.phpf Uf K1Jphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Keys.php$U$wͶKphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Click.php U ^aNphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Location.php U Uphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/GenericAccessor.phpT +UT +XJphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/File.phpU0ߗQphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/AcceptAlert.php1 +U1 +BHOphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/AlertText.phpC UC lXimIphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Url.php U KLphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Window.php +U +Vphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/GenericAttribute.php +U +BLphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/MoveTo.phpU- +Qphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Orientation.php U @\WIIphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Log.php U "=Lphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Active.php + U + 6Rphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/DismissAlert.php6 +U6 +vlKphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionCommand/Frame.php U z":phar/phpunit-selenium/Extensions/Selenium2TestCase/URL.phpUHFFphar/phpunit-selenium/Extensions/Selenium2TestCase/SessionStrategy.php U ̳rAphar/phpunit-selenium/Extensions/Selenium2TestCase/KeysHolder.phpU|HGphar/phpunit-selenium/Extensions/Selenium2TestCase/Element/Accessor.phpU\wEphar/phpunit-selenium/Extensions/Selenium2TestCase/Element/Select.phpUL@">phar/phpunit-selenium/Extensions/Selenium2TestCase/Session.php!1U!1 ;phar/phpunit-selenium/Extensions/SeleniumCommon/prepend.php? U? :phar/phpunit-selenium/Extensions/SeleniumCommon/append.php U Bphar/phpunit-selenium/Extensions/SeleniumCommon/RemoteCoverage.phpIUI\.?phar/phpunit-selenium/Extensions/SeleniumCommon/ExitHandler.php*U*϶Dphar/phpunit-selenium/Extensions/SeleniumCommon/phpunit_coverage.php`U`Om9phar/phpunit-selenium/Extensions/SeleniumBrowserSuite.phpUD6phar/phpunit-selenium/Extensions/Selenium2TestCase.php!EU!E[,phar/php-code-coverage/CodeCoverage/Util.phpUK϶1phar/php-code-coverage/CodeCoverage/Exception.phpUK)<phar/php-code-coverage/CodeCoverage/Report/HTML/Renderer.phpEUEV:Fphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Dashboard.php&U&b;Aphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/File.php,LU,L=>Uphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/dashboard.html.distU{Uphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/directory.html.disteUeǐXphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/coverage_bar.html.dist1U1itLUphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/bootstrap.min.jsoUo;Nphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/d3.min.jsUNUUN;1Sphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/respond.min.jsU{Uphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/html5shiv.min.jsL +UL +FRphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/holder.min.jsmUmJsѶQphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/nv.d3.min.js]U]']4Rphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/js/jquery.min.jsvUveUphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/file_item.html.distgUgV PWphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/method_item.html.distxUx*Zphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/directory_item.html.dist5U5Z]Pphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/file.html.dist +U +DOphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/css/style.css+U+Y`gSphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/css/nv.d3.min.cssX%UX%0,Wphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/css/bootstrap.min.css9U9ܛ2hphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.ttf\U\<jphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.woff2lFUlFvaiphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.woff[U[{hphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.eotNUNXDZhphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Template/fonts/glyphicons-halflings-regular.svg¨U¨|ɶFphar/php-code-coverage/CodeCoverage/Report/HTML/Renderer/Directory.php| U| 5phar/php-code-coverage/CodeCoverage/Report/Crap4j.php-U-{+ض8phar/php-code-coverage/CodeCoverage/Report/XML/Tests.phpU2t7phar/php-code-coverage/CodeCoverage/Report/XML/File.php0U0ڶ7phar/php-code-coverage/CodeCoverage/Report/XML/Node.php^U^N:phar/php-code-coverage/CodeCoverage/Report/XML/Project.php]U]h>9phar/php-code-coverage/CodeCoverage/Report/XML/Totals.phpUoF<phar/php-code-coverage/CodeCoverage/Report/XML/Directory.phpUZ>phar/php-code-coverage/CodeCoverage/Report/XML/File/Method.phpuUuʶ>phar/php-code-coverage/CodeCoverage/Report/XML/File/Report.phpCUCY{ȶ@phar/php-code-coverage/CodeCoverage/Report/XML/File/Coverage.php5U5(+R<phar/php-code-coverage/CodeCoverage/Report/XML/File/Unit.phpB +UB + kr3phar/php-code-coverage/CodeCoverage/Report/Node.phpU2޶<phar/php-code-coverage/CodeCoverage/Report/Node/Iterator.phpUMq8phar/php-code-coverage/CodeCoverage/Report/Node/File.phpGUG`=phar/php-code-coverage/CodeCoverage/Report/Node/Directory.phpS(US(5phar/php-code-coverage/CodeCoverage/Report/Clover.php'U'dV3phar/php-code-coverage/CodeCoverage/Report/HTML.php=U=06phar/php-code-coverage/CodeCoverage/Report/Factory.phpkUkrp22phar/php-code-coverage/CodeCoverage/Report/XML.phplUls2phar/php-code-coverage/CodeCoverage/Report/PHP.phpU4R}3phar/php-code-coverage/CodeCoverage/Report/Text.phpx!Ux!`kLphar/php-code-coverage/CodeCoverage/Exception/UnintentionallyCoveredCode.phpU}.phar/php-code-coverage/CodeCoverage/Driver.phpU0.phar/php-code-coverage/CodeCoverage/Filter.phpU#Bphar/php-code-coverage/CodeCoverage/Util/InvalidArgumentHelper.php=U=3phar/php-code-coverage/CodeCoverage/Driver/HHVM.php^U^7b5phar/php-code-coverage/CodeCoverage/Driver/PHPDBG.php U 95phar/php-code-coverage/CodeCoverage/Driver/Xdebug.phpx Ux phar/php-code-coverage/LICENSEUЉxZ'phar/php-code-coverage/CodeCoverage.phpriUri*phar/symfony/LICENSE)U)&phar/symfony/yaml/Inline.phpbMUbMoܗphar/symfony/yaml/Unescaper.phpUmphar/symfony/yaml/Yaml.php,U, .phar/symfony/yaml/Exception/ParseException.phpU79-phar/symfony/yaml/Exception/DumpException.phpUؙ՚0phar/symfony/yaml/Exception/RuntimeException.phpU|-2phar/symfony/yaml/Exception/ExceptionInterface.phpU+lphar/symfony/yaml/Parser.phphUhΖphar/symfony/yaml/Dumper.php U lDphar/symfony/yaml/Escaper.phpUK phar/ca.pemUby"phar/doctrine-instantiator/LICENSE$U$ +͂Jphar/doctrine-instantiator/Doctrine/Instantiator/InstantiatorInterface.php~U~:Qphar/doctrine-instantiator/Doctrine/Instantiator/Exception/ExceptionInterface.phpU.öWphar/doctrine-instantiator/Doctrine/Instantiator/Exception/InvalidArgumentException.phpUh7IWphar/doctrine-instantiator/Doctrine/Instantiator/Exception/UnexpectedValueException.php +U +" Aphar/doctrine-instantiator/Doctrine/Instantiator/Instantiator.php U &phar/php-token-stream/LICENSEU-& 5phar/php-token-stream/Token/Stream/CachingFactory.phpU_&phar/php-token-stream/Token/Stream.php@U@  phar/php-token-stream/Token.php:bU:b#0!-phar/dbunit/Extensions/Database/Exception.phpUm-`5phar/dbunit/Extensions/Database/UI/IMediumPrinter.phpU}0.phar/dbunit/Extensions/Database/UI/Command.phpIUIT3phar/dbunit/Extensions/Database/UI/Mediums/Text.php U o4a;phar/dbunit/Extensions/Database/UI/InvalidModeException.phpU*,,phar/dbunit/Extensions/Database/UI/IMode.phpUd.phar/dbunit/Extensions/Database/UI/Context.phpUGDphar/dbunit/Extensions/Database/UI/Modes/ExportDataSet/Arguments.php +U +(ΰ:phar/dbunit/Extensions/Database/UI/Modes/ExportDataSet.php U {.phar/dbunit/Extensions/Database/UI/IMedium.phpPUPԚ2phar/dbunit/Extensions/Database/UI/ModeFactory.phpB +UB +ң3phar/dbunit/Extensions/Database/UI/IModeFactory.php +U +9phar/dbunit/Extensions/Database/IDatabaseListConsumer.php;U;4o7phar/dbunit/Extensions/Database/Operation/Exception.php/U/J5phar/dbunit/Extensions/Database/Operation/Replace.phpeUeOئ4phar/dbunit/Extensions/Database/Operation/Insert.phpUEP4phar/dbunit/Extensions/Database/Operation/Delete.phpUn@phar/dbunit/Extensions/Database/Operation/IDatabaseOperation.phpU"2phar/dbunit/Extensions/Database/Operation/Null.phpU0Ӷ7phar/dbunit/Extensions/Database/Operation/DeleteAll.phpUCi4phar/dbunit/Extensions/Database/Operation/Update.phpU>%6phar/dbunit/Extensions/Database/Operation/RowBased.php5U5ih5phar/dbunit/Extensions/Database/Operation/Factory.php U ̶7phar/dbunit/Extensions/Database/Operation/Composite.phpU)ʶ6phar/dbunit/Extensions/Database/Operation/Truncate.php U rC,phar/dbunit/Extensions/Database/TestCase.php"U"oj2phar/dbunit/Extensions/Database/AbstractTester.phpUCY6phar/dbunit/Extensions/Database/DB/MetaData/Sqlite.php3 +U3 +-Բ5phar/dbunit/Extensions/Database/DB/MetaData/Dblib.php U Q5phar/dbunit/Extensions/Database/DB/MetaData/MySQL.phpU7-5Aphar/dbunit/Extensions/Database/DB/MetaData/InformationSchema.phpiUiTϙ8phar/dbunit/Extensions/Database/DB/MetaData/Firebird.phpU`3phar/dbunit/Extensions/Database/DB/MetaData/Oci.phpbUb_5phar/dbunit/Extensions/Database/DB/MetaData/PgSQL.phpUӶ6phar/dbunit/Extensions/Database/DB/MetaData/SqlSrv.php U ,phar/dbunit/Extensions/Database/DB/Table.phpU8k8o6phar/dbunit/Extensions/Database/DB/FilteredDataSet.phpFUF (e:phar/dbunit/Extensions/Database/DB/IDatabaseConnection.php U dʶ4phar/dbunit/Extensions/Database/DB/TableMetaData.phpU;Е/phar/dbunit/Extensions/Database/DB/MetaData.phpUET@phar/dbunit/Extensions/Database/DB/DefaultDatabaseConnection.php}U}0phar/dbunit/Extensions/Database/DB/IMetaData.php3U3@G4phar/dbunit/Extensions/Database/DB/TableIterator.php} +U} +0W6.phar/dbunit/Extensions/Database/DB/DataSet.phpRURx¶5phar/dbunit/Extensions/Database/DB/ResultSetTable.phpU͛1phar/dbunit/Extensions/Database/DefaultTester.phpUX`;phar/dbunit/Extensions/Database/Constraint/TableIsEqual.phpU7 +=phar/dbunit/Extensions/Database/Constraint/DataSetIsEqual.phpUM<phar/dbunit/Extensions/Database/Constraint/TableRowCount.phpUIXӶ+phar/dbunit/Extensions/Database/ITester.phpU#;phar/dbunit/Extensions/Database/DataSet/MysqlXmlDataSet.php'U'J!8phar/dbunit/Extensions/Database/DataSet/QueryDataSet.php U Vö7phar/dbunit/Extensions/Database/DataSet/YamlDataSet.phpU_;phar/dbunit/Extensions/Database/DataSet/AbstractDataSet.php#U#[Gζ1phar/dbunit/Extensions/Database/DataSet/ISpec.phpU݂7phar/dbunit/Extensions/Database/DataSet/TableFilter.php U װhAphar/dbunit/Extensions/Database/DataSet/AbstractTableMetaData.phpU;c<phar/dbunit/Extensions/Database/DataSet/CompositeDataSet.php: +U: +! @phar/dbunit/Extensions/Database/DataSet/DefaultTableIterator.php U 49phar/dbunit/Extensions/Database/DataSet/DataSetFilter.phpjUj)7phar/dbunit/Extensions/Database/DataSet/IYamlParser.phpU@ۈ[6phar/dbunit/Extensions/Database/DataSet/XmlDataSet.phpUJz:phar/dbunit/Extensions/Database/DataSet/DefaultDataSet.phpUWJ=phar/dbunit/Extensions/Database/DataSet/SymfonyYamlParser.phpOUOPW4phar/dbunit/Extensions/Database/DataSet/IDataSet.phpUZ߶:phar/dbunit/Extensions/Database/DataSet/ITableMetaData.php1U1,+;?phar/dbunit/Extensions/Database/DataSet/Persistors/Abstract.php +U +D::phar/dbunit/Extensions/Database/DataSet/Persistors/Xml.php5 U5 .>phar/dbunit/Extensions/Database/DataSet/Persistors/FlatXml.php U TZ,;phar/dbunit/Extensions/Database/DataSet/Persistors/Yaml.phpU?phar/dbunit/Extensions/Database/DataSet/Persistors/MysqlXml.php U OӶ>phar/dbunit/Extensions/Database/DataSet/Persistors/Factory.phpU(6phar/dbunit/Extensions/Database/DataSet/CsvDataSet.php U Dphar/dbunit/Extensions/Database/DataSet/ReplacementTableIterator.php U ڋ>phar/dbunit/Extensions/Database/DataSet/ReplacementDataSet.php +U + 8phar/dbunit/Extensions/Database/DataSet/ArrayDataSet.phpyUy[8phar/dbunit/Extensions/Database/DataSet/DefaultTable.phpU!t:phar/dbunit/Extensions/Database/DataSet/ITableIterator.phpU !-:phar/dbunit/Extensions/Database/DataSet/FlatXmlDataSet.phpOUO36K8phar/dbunit/Extensions/Database/DataSet/IPersistable.phpU>phar/dbunit/Extensions/Database/DataSet/AbstractXmlDataSet.php U Q6?phar/dbunit/Extensions/Database/DataSet/TableMetaDataFilter.phpR UR @<phar/dbunit/Extensions/Database/DataSet/ReplacementTable.php|U|hRd9phar/dbunit/Extensions/Database/DataSet/Specs/DbQuery.php U w5phar/dbunit/Extensions/Database/DataSet/Specs/Xml.phpUTZ9phar/dbunit/Extensions/Database/DataSet/Specs/FlatXml.phpU߆6phar/dbunit/Extensions/Database/DataSet/Specs/Yaml.phpU{;>:phar/dbunit/Extensions/Database/DataSet/Specs/IFactory.phplUl՗+5phar/dbunit/Extensions/Database/DataSet/Specs/Csv.php +U +9phar/dbunit/Extensions/Database/DataSet/Specs/DbTable.phpUJ9phar/dbunit/Extensions/Database/DataSet/Specs/Factory.phpUA̶6phar/dbunit/Extensions/Database/DataSet/QueryTable.php`U` 9phar/dbunit/Extensions/Database/DataSet/AbstractTable.phpXUX2phar/dbunit/Extensions/Database/DataSet/ITable.phpUȃN@phar/dbunit/Extensions/Database/DataSet/DefaultTableMetaData.phpUضphar/php-invoker/Invoker.phpU w%phar/php-invoker/TimeoutException.phppUp~!phar/phpunit-mock-objects/LICENSEUC>:phar/phpunit-mock-objects/Framework/MockObject/Matcher.php U 7/=phar/phpunit-mock-objects/Framework/MockObject/Verifiable.phpU'L<phar/phpunit-mock-objects/Framework/MockObject/Invokable.php9U9~>phar/phpunit-mock-objects/Framework/MockObject/MockBuilder.phpU\_Fphar/phpunit-mock-objects/Framework/MockObject/Exception/Exception.phpU]TSphar/phpunit-mock-objects/Framework/MockObject/Exception/BadMethodCallException.phpU).Mphar/phpunit-mock-objects/Framework/MockObject/Exception/RuntimeException.phpUYn4=phar/phpunit-mock-objects/Framework/MockObject/Invocation.phpUs=phar/phpunit-mock-objects/Framework/MockObject/MockObject.php9U9CCphar/phpunit-mock-objects/Framework/MockObject/InvocationMocker.phpU2׶Ephar/phpunit-mock-objects/Framework/MockObject/Matcher/MethodName.php U vMphar/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtLeastOnce.phpUcIHphar/phpunit-mock-objects/Framework/MockObject/Matcher/AnyParameters.phpCUCEphar/phpunit-mock-objects/Framework/MockObject/Matcher/Parameters.phpUPjJphar/phpunit-mock-objects/Framework/MockObject/Matcher/AnyInvokedCount.phpUDs$Nphar/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtLeastCount.phpU REphar/phpunit-mock-objects/Framework/MockObject/Matcher/Invocation.phpUKbPphar/phpunit-mock-objects/Framework/MockObject/Matcher/ConsecutiveParameters.phpUiOmIphar/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtIndex.php U YGphar/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedCount.php U SGMphar/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedAtMostCount.phpUJphar/phpunit-mock-objects/Framework/MockObject/Matcher/InvokedRecorder.php`U`!Nphar/phpunit-mock-objects/Framework/MockObject/Matcher/StatelessInvocation.phpkUk϶<phar/phpunit-mock-objects/Framework/MockObject/Generator.phpUMphar/phpunit-mock-objects/Framework/MockObject/Generator/wsdl_method.tpl.dist<U<iPphar/phpunit-mock-objects/Framework/MockObject/Generator/unmocked_clone.tpl.distU8W}ضNphar/phpunit-mock-objects/Framework/MockObject/Generator/mocked_class.tpl.dist U FZNphar/phpunit-mock-objects/Framework/MockObject/Generator/mocked_clone.tpl.distUaTPphar/phpunit-mock-objects/Framework/MockObject/Generator/proxied_method.tpl.distU?aLphar/phpunit-mock-objects/Framework/MockObject/Generator/wsdl_class.tpl.distUw&SMphar/phpunit-mock-objects/Framework/MockObject/Generator/trait_class.tpl.dist7U7[$~Ophar/phpunit-mock-objects/Framework/MockObject/Generator/mocked_method.tpl.distUbVUphar/phpunit-mock-objects/Framework/MockObject/Generator/mocked_class_method.tpl.distU4޶Vphar/phpunit-mock-objects/Framework/MockObject/Generator/mocked_static_method.tpl.distU+F@phar/phpunit-mock-objects/Framework/MockObject/Builder/Match.phpWUWEACphar/phpunit-mock-objects/Framework/MockObject/Builder/Identity.phpU(4Kphar/phpunit-mock-objects/Framework/MockObject/Builder/InvocationMocker.phpvUvJphar/phpunit-mock-objects/Framework/MockObject/Builder/ParametersMatch.phpUsґζDphar/phpunit-mock-objects/Framework/MockObject/Builder/Namespace.phpUM쉔Jphar/phpunit-mock-objects/Framework/MockObject/Builder/MethodNameMatch.php)U)s?phar/phpunit-mock-objects/Framework/MockObject/Builder/Stub.phpoUohrDphar/phpunit-mock-objects/Framework/MockObject/Invocation/Object.phpU9Dphar/phpunit-mock-objects/Framework/MockObject/Invocation/Static.phpFUFKp7phar/phpunit-mock-objects/Framework/MockObject/Stub.php\U\f+FжAphar/phpunit-mock-objects/Framework/MockObject/Stub/Exception.phpU<>phar/phpunit-mock-objects/Framework/MockObject/Stub/Return.phpULf+Bphar/phpunit-mock-objects/Framework/MockObject/Stub/ReturnSelf.phpUIFphar/phpunit-mock-objects/Framework/MockObject/Stub/ReturnValueMap.php|U|uB Hphar/phpunit-mock-objects/Framework/MockObject/Stub/ConsecutiveCalls.phpU2 Iphar/phpunit-mock-objects/Framework/MockObject/Stub/MatcherCollection.php:U:YFphar/phpunit-mock-objects/Framework/MockObject/Stub/ReturnCallback.phpUg`Fphar/phpunit-mock-objects/Framework/MockObject/Stub/ReturnArgument.phpU9)`phar/phpunit/Exception.phpsUsy:۶phar/phpunit/Framework/Test.phpU;$phar/phpunit/Framework/Exception.php U D&phar/phpunit/Framework/OutputError.phpUj̶!phar/phpunit/Framework/Assert.php…U…`7 +0phar/phpunit/Framework/CodeCoverageException.phpqUqE$phar/phpunit/Framework/TestSuite.phpsUs{$phar/phpunit/Framework/RiskyTest.phpU\)phar/phpunit/Framework/SelfDescribing.phpU:|+phar/phpunit/Framework/Assert/Functions.php!U!-0ǂ)phar/phpunit/Framework/SyntheticError.phpUƷ)phar/phpunit/Framework/RiskyTestError.phpU?v'phar/phpunit/Framework/TestListener.phpR +UR +ݶ+phar/phpunit/Framework/ExceptionWrapper.phpU&phar/phpunit/Framework/SkippedTest.phpU_ $7phar/phpunit/Framework/InvalidCoversTargetException.phpU%M#phar/phpunit/Framework/TestCase.phpU3u5phar/phpunit/Framework/ExpectationFailedException.phpU_S; phar/phpunit/Framework/Error.php$U$X%yW%phar/phpunit/Framework/Constraint.phpUWSǶ/phar/phpunit/Framework/AssertionFailedError.php{U{+-phar/phpunit/Framework/IncompleteTestCase.php5U5.S1phar/phpunit/Framework/TestSuite/DataProvider.phpUU>o,phar/phpunit/Framework/Constraint/IsTrue.phpkUk,phar/phpunit/Framework/Constraint/IsJson.phpUC\-phar/phpunit/Framework/Constraint/IsEqual.phpU#!ڶ)phar/phpunit/Framework/Constraint/Xor.php9 U9 suFphar/phpunit/Framework/Constraint/JsonMatches/ErrorMessageProvider.phpUWJ3phar/phpunit/Framework/Constraint/StringMatches.php U %c)phar/phpunit/Framework/Constraint/Not.phpU@ζ<phar/phpunit/Framework/Constraint/ExceptionMessageRegExp.phpUUU`]_1phar/phpunit/Framework/Constraint/GreaterThan.phpU ?n6phar/phpunit/Framework/Constraint/ExceptionMessage.php8U8Xz.phar/phpunit/Framework/Constraint/Callback.php:U:+1phar/phpunit/Framework/Constraint/ArrayHasKey.phpUy64phar/phpunit/Framework/Constraint/StringContains.php,U,Gzڶ=phar/phpunit/Framework/Constraint/TraversableContainsOnly.php +U +}=-phar/phpunit/Framework/Constraint/IsEmpty.phpU82/phar/phpunit/Framework/Constraint/PCREMatch.phpU^#,phar/phpunit/Framework/Constraint/IsNull.phpkUkK:/phar/phpunit/Framework/Constraint/Attribute.php U LLp-phar/phpunit/Framework/Constraint/IsFalse.phpoUo?A=phar/phpunit/Framework/Constraint/ClassHasStaticAttribute.phpwUwRQp(phar/phpunit/Framework/Constraint/Or.php U ^|1phar/phpunit/Framework/Constraint/IsIdentical.php7U7ܶ0phar/phpunit/Framework/Constraint/FileExists.phpU I/phar/phpunit/Framework/Constraint/Composite.php+U+IY6phar/phpunit/Framework/Constraint/StringStartsWith.phpU|C}4phar/phpunit/Framework/Constraint/StringEndsWith.phpU^0phar/phpunit/Framework/Constraint/IsAnything.phpU;+phar/phpunit/Framework/Constraint/Count.php U !w)phar/phpunit/Framework/Constraint/And.php U A8+phar/phpunit/Framework/BaseTestListener.phppUpMU+phar/phpunit/Framework/SkippedTestError.php U T:phar/phpunit/Framework/UnintentionallyCoveredCodeError.phpUd^S"phar/phpunit/Framework/Warning.phpUS*phar/phpunit/Extensions/GroupTestSuite.phpU6)phar/phpunit/Extensions/PhptTestSuite.phpUH(phar/phpunit/Extensions/RepeatedTest.phpU,a*phar/phpunit/Extensions/TicketListener.phpU)phar/phpunit/Extensions/TestDecorator.php: U: 0v-(phar/phpunit/Extensions/PhptTestCase.php(U(*Vphar/phpunit/TextUI/Command.phpUK"phar/phpunit/TextUI/TestRunner.phphUh2"%phar/phpunit/TextUI/ResultPrinter.phpCUC/phar/phpunit/Util/Test.phpcUcz`& phar/phpunit/Util/Filesystem.phpU(`Ophar/phpunit/Util/Getopt.phpU}h"phar/phpunit/Util/ErrorHandler.php U Qݶphar/phpunit/Util/String.phpsUsU)D+phar/phpunit/Util/InvalidArgumentHelper.phpUW4phar/phpunit/Util/Printer.php^U^ + #phar/phpunit/Util/Configuration.phpƊUƊADֶ!phar/phpunit/Util/GlobalState.php/U/KU|phar/phpunit/Util/Log/JSON.php%U%ڶphar/phpunit/Util/Log/TAP.phpU|%nphar/phpunit/Util/Log/JUnit.php2U2, phar/phpunit/Util/Blacklist.phpU!zWI'phar/phpunit/Util/TestSuiteIterator.phpU⍘phar/phpunit/Util/Filter.php U pĶ0phar/phpunit/Util/TestDox/ResultPrinter/HTML.phpUpfʶ0phar/phpunit/Util/TestDox/ResultPrinter/Text.phpwUw ;+phar/phpunit/Util/TestDox/ResultPrinter.phpOUO l,phar/phpunit/Util/TestDox/NamePrettifier.phpE UE ߤphar/phpunit/Util/Regex.phpUPdphar/phpunit/Util/Type.phpUC!Ҷ!phar/phpunit/Util/PHP/Default.phpU>>L6phar/phpunit/Util/PHP/Template/TestCaseMethod.tpl.distU!phar/phpunit/Util/PHP/Windows.phpx +Ux +" phar/phpunit/Util/Fileloader.phpU)عphar/phpunit/Util/XML.phprUr|@phar/phpunit/Util/PHP.phpU]QF!phar/phpunit/Runner/Exception.phptUtd#phar/phpunit/Runner/Filter/Test.php U a,phar/phpunit/Runner/Filter/Group/Exclude.phpUPF,phar/phpunit/Runner/Filter/Group/Include.phpUVe&phar/phpunit/Runner/Filter/Factory.phpUJ$phar/phpunit/Runner/Filter/Group.phpUc2{m'phar/phpunit/Runner/TestSuiteLoader.phpU9ɶ&phar/phpunit/Runner/BaseTestRunner.phpUQ/phar/phpunit/Runner/StandardTestSuiteLoader.php U -phar/phpunit/Runner/Version.phpU="phar/sebastian-environment/LICENSE +U +߶&phar/sebastian-environment/Runtime.php@U@Ҏ!˶&phar/sebastian-environment/Console.phppUp .Q.phar/phpdocumentor-reflection-docblock/LICENSE8U8ʶLphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock.phpc6Uc6qUphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Location.phpqUqu/Yphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/SinceTag.php|U|tRWphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/SeeTag.phpUi?$[phar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/VersionTag.phpx +Ux +Eƶ\phar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/PropertyTag.phpOUO#mZphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/ReturnTag.phpUpS^phar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/DeprecatedTag.phpUK Zphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/ThrowsTag.phpLUL"Zphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/CoversTag.phpIUI9{Zphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/SourceTag.php U n*Xphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/UsesTag.phpEUE.˶Zphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/MethodTag.php~U~!kͶ`phar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/PropertyReadTag.php[U[< =Zphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/AuthorTag.phpM UM 1Wphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/VarTag.phpEUEͶXphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/LinkTag.phpLULF[phar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/ExampleTag.phpU +Yphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/ParamTag.phpH UH  aphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag/PropertyWriteTag.php]U]RpWphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Serializer.phpUrжTphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Context.php5U5l.%\phar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Type/Collection.phpU+=4Pphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Tag.php(U(E6 Xphar/phpdocumentor-reflection-docblock/phpDocumentor/Reflection/DocBlock/Description.phpUԲضphar/sebastian-version/LICENSEUn"phar/sebastian-version/Version.php2U2BZ. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ + +namespace SebastianBergmann\GlobalState; + +/** + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ +interface Exception +{ +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ + +namespace SebastianBergmann\GlobalState; + +use Closure; +use ReflectionClass; + +/** + * A snapshot of global state. + * + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ +class Snapshot +{ + /** + * @var Blacklist + */ + private $blacklist; + + /** + * @var array + */ + private $globalVariables = array(); + + /** + * @var array + */ + private $superGlobalArrays = array(); + + /** + * @var array + */ + private $superGlobalVariables = array(); + + /** + * @var array + */ + private $staticAttributes = array(); + + /** + * @var array + */ + private $iniSettings = array(); + + /** + * @var array + */ + private $includedFiles = array(); + + /** + * @var array + */ + private $constants = array(); + + /** + * @var array + */ + private $functions = array(); + + /** + * @var array + */ + private $interfaces = array(); + + /** + * @var array + */ + private $classes = array(); + + /** + * @var array + */ + private $traits = array(); + + /** + * Creates a snapshot of the current global state. + * + * @param Blacklist $blacklist + * @param boolean $includeGlobalVariables + * @param boolean $includeStaticAttributes + * @param boolean $includeConstants + * @param boolean $includeFunctions + * @param boolean $includeClasses + * @param boolean $includeInterfaces + * @param boolean $includeTraits + * @param boolean $includeIniSettings + * @param boolean $includeIncludedFiles + */ + public function __construct(Blacklist $blacklist = null, $includeGlobalVariables = true, $includeStaticAttributes = true, $includeConstants = true, $includeFunctions = true, $includeClasses = true, $includeInterfaces = true, $includeTraits = true, $includeIniSettings = true, $includeIncludedFiles = true) + { + if ($blacklist === null) { + $blacklist = new Blacklist; + } + + $this->blacklist = $blacklist; + + if ($includeConstants) { + $this->snapshotConstants(); + } + + if ($includeFunctions) { + $this->snapshotFunctions(); + } + + if ($includeClasses || $includeStaticAttributes) { + $this->snapshotClasses(); + } + + if ($includeInterfaces) { + $this->snapshotInterfaces(); + } + + if ($includeGlobalVariables) { + $this->setupSuperGlobalArrays(); + $this->snapshotGlobals(); + } + + if ($includeStaticAttributes) { + $this->snapshotStaticAttributes(); + } + + if ($includeIniSettings) { + $this->iniSettings = ini_get_all(null, false); + } + + if ($includeIncludedFiles) { + $this->includedFiles = get_included_files(); + } + + if (function_exists('get_declared_traits')) { + $this->traits = get_declared_traits(); + } + } + + /** + * @return Blacklist + */ + public function blacklist() + { + return $this->blacklist; + } + + /** + * @return array + */ + public function globalVariables() + { + return $this->globalVariables; + } + + /** + * @return array + */ + public function superGlobalVariables() + { + return $this->superGlobalVariables; + } + + /** + * Returns a list of all super-global variable arrays. + * + * @return array + */ + public function superGlobalArrays() + { + return $this->superGlobalArrays; + } + + /** + * @return array + */ + public function staticAttributes() + { + return $this->staticAttributes; + } + + /** + * @return array + */ + public function iniSettings() + { + return $this->iniSettings; + } + + /** + * @return array + */ + public function includedFiles() + { + return $this->includedFiles; + } + + /** + * @return array + */ + public function constants() + { + return $this->constants; + } + + /** + * @return array + */ + public function functions() + { + return $this->functions; + } + + /** + * @return array + */ + public function interfaces() + { + return $this->interfaces; + } + + /** + * @return array + */ + public function classes() + { + return $this->classes; + } + + /** + * @return array + */ + public function traits() + { + return $this->traits; + } + + /** + * Creates a snapshot user-defined constants. + */ + private function snapshotConstants() + { + $constants = get_defined_constants(true); + + if (isset($constants['user'])) { + $this->constants = $constants['user']; + } + } + + /** + * Creates a snapshot user-defined functions. + */ + private function snapshotFunctions() + { + $functions = get_defined_functions(); + + $this->functions = $functions['user']; + } + + /** + * Creates a snapshot user-defined classes. + */ + private function snapshotClasses() + { + foreach (array_reverse(get_declared_classes()) as $className) { + $class = new ReflectionClass($className); + + if (!$class->isUserDefined()) { + break; + } + + $this->classes[] = $className; + } + + $this->classes = array_reverse($this->classes); + } + + /** + * Creates a snapshot user-defined interfaces. + */ + private function snapshotInterfaces() + { + foreach (array_reverse(get_declared_interfaces()) as $interfaceName) { + $class = new ReflectionClass($interfaceName); + + if (!$class->isUserDefined()) { + break; + } + + $this->interfaces[] = $interfaceName; + } + + $this->interfaces = array_reverse($this->interfaces); + } + + /** + * Creates a snapshot of all global and super-global variables. + */ + private function snapshotGlobals() + { + $superGlobalArrays = $this->superGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + $this->snapshotSuperGlobalArray($superGlobalArray); + } + + foreach (array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !in_array($key, $superGlobalArrays) && + $this->canBeSerialized($GLOBALS[$key]) && + !$this->blacklist->isGlobalVariableBlacklisted($key)) { + $this->globalVariables[$key] = unserialize(serialize($GLOBALS[$key])); + } + } + } + + /** + * Creates a snapshot a super-global variable array. + * + * @param $superGlobalArray + */ + private function snapshotSuperGlobalArray($superGlobalArray) + { + $this->superGlobalVariables[$superGlobalArray] = array(); + + if (isset($GLOBALS[$superGlobalArray]) && is_array($GLOBALS[$superGlobalArray])) { + foreach ($GLOBALS[$superGlobalArray] as $key => $value) { + $this->superGlobalVariables[$superGlobalArray][$key] = unserialize(serialize($value)); + } + } + } + + /** + * Creates a snapshot of all static attributes in user-defined classes. + */ + private function snapshotStaticAttributes() + { + foreach ($this->classes as $className) { + $class = new ReflectionClass($className); + $snapshot = array(); + + foreach ($class->getProperties() as $attribute) { + if ($attribute->isStatic()) { + $name = $attribute->getName(); + + if ($this->blacklist->isStaticAttributeBlacklisted($className, $name)) { + continue; + } + + $attribute->setAccessible(true); + $value = $attribute->getValue(); + + if ($this->canBeSerialized($value)) { + $snapshot[$name] = unserialize(serialize($value)); + } + } + } + + if (!empty($snapshot)) { + $this->staticAttributes[$className] = $snapshot; + } + } + } + + /** + * Returns a list of all super-global variable arrays. + * + * @return array + */ + private function setupSuperGlobalArrays() + { + $this->superGlobalArrays = array( + '_ENV', + '_POST', + '_GET', + '_COOKIE', + '_SERVER', + '_FILES', + '_REQUEST' + ); + + if (ini_get('register_long_arrays') == '1') { + $this->superGlobalArrays = array_merge( + $this->superGlobalArrays, + array( + 'HTTP_ENV_VARS', + 'HTTP_POST_VARS', + 'HTTP_GET_VARS', + 'HTTP_COOKIE_VARS', + 'HTTP_SERVER_VARS', + 'HTTP_POST_FILES' + ) + ); + } + } + + /** + * @param mixed $variable + * @return boolean + * @todo Implement this properly + */ + private function canBeSerialized($variable) { + return !$variable instanceof Closure; + } +} +GlobalState + +Copyright (c) 2001-2014, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, 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. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ + +namespace SebastianBergmann\GlobalState; + +use ReflectionProperty; + +/** + * Restorer of snapshots of global state. + * + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ +class Restorer +{ + /** + * Deletes function definitions that are not defined in a snapshot. + * + * @param Snapshot $snapshot + * @throws RuntimeException when the uopz_delete() function is not available + * @see https://github.com/krakjoe/uopz + */ + public function restoreFunctions(Snapshot $snapshot) + { + if (!function_exists('uopz_delete')) { + throw new RuntimeException('The uopz_delete() function is required for this operation'); + } + + $functions = get_defined_functions(); + + foreach (array_diff($functions['user'], $snapshot->functions()) as $function) { + uopz_delete($function); + } + } + + /** + * Restores all global and super-global variables from a snapshot. + * + * @param Snapshot $snapshot + */ + public function restoreGlobalVariables(Snapshot $snapshot) + { + $superGlobalArrays = $snapshot->superGlobalArrays(); + + foreach ($superGlobalArrays as $superGlobalArray) { + $this->restoreSuperGlobalArray($snapshot, $superGlobalArray); + } + + $globalVariables = $snapshot->globalVariables(); + + foreach (array_keys($GLOBALS) as $key) { + if ($key != 'GLOBALS' && + !in_array($key, $superGlobalArrays) && + !$snapshot->blacklist()->isGlobalVariableBlacklisted($key)) { + if (isset($globalVariables[$key])) { + $GLOBALS[$key] = $globalVariables[$key]; + } else { + unset($GLOBALS[$key]); + } + } + } + } + + /** + * Restores all static attributes in user-defined classes from this snapshot. + * + * @param Snapshot $snapshot + */ + public function restoreStaticAttributes(Snapshot $snapshot) + { + foreach ($snapshot->staticAttributes() as $className => $staticAttributes) { + foreach ($staticAttributes as $name => $value) { + $reflector = new ReflectionProperty($className, $name); + $reflector->setAccessible(true); + $reflector->setValue($value); + } + } + } + + /** + * Restores a super-global variable array from this snapshot. + * + * @param Snapshot $snapshot + * @param $superGlobalArray + */ + private function restoreSuperGlobalArray(Snapshot $snapshot, $superGlobalArray) + { + $superGlobalVariables = $snapshot->superGlobalVariables(); + + if (isset($GLOBALS[$superGlobalArray]) && + is_array($GLOBALS[$superGlobalArray]) && + isset($superGlobalVariables[$superGlobalArray])) { + $keys = array_keys( + array_merge( + $GLOBALS[$superGlobalArray], + $superGlobalVariables[$superGlobalArray] + ) + ); + + foreach ($keys as $key) { + if (isset($superGlobalVariables[$superGlobalArray][$key])) { + $GLOBALS[$superGlobalArray][$key] = $superGlobalVariables[$superGlobalArray][$key]; + } else { + unset($GLOBALS[$superGlobalArray][$key]); + } + } + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ + +namespace SebastianBergmann\GlobalState; + +use ReflectionClass; + +/** + * A blacklist for global state elements that should not be snapshotted. + * + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ +class Blacklist +{ + /** + * @var array + */ + private $globalVariables = array(); + + /** + * @var array + */ + private $classes = array(); + + /** + * @var array + */ + private $classNamePrefixes = array(); + + /** + * @var array + */ + private $parentClasses = array(); + + /** + * @var array + */ + private $interfaces = array(); + + /** + * @var array + */ + private $staticAttributes = array(); + + /** + * @param string $variableName + */ + public function addGlobalVariable($variableName) + { + $this->globalVariables[$variableName] = true; + } + + /** + * @param string $className + */ + public function addClass($className) + { + $this->classes[] = $className; + } + + /** + * @param string $className + */ + public function addSubclassesOf($className) + { + $this->parentClasses[] = $className; + } + + /** + * @param string $interfaceName + */ + public function addImplementorsOf($interfaceName) + { + $this->interfaces[] = $interfaceName; + } + + /** + * @param string $classNamePrefix + */ + public function addClassNamePrefix($classNamePrefix) + { + $this->classNamePrefixes[] = $classNamePrefix; + } + + /** + * @param string $className + * @param string $attributeName + */ + public function addStaticAttribute($className, $attributeName) + { + if (!isset($this->staticAttributes[$className])) { + $this->staticAttributes[$className] = array(); + } + + $this->staticAttributes[$className][$attributeName] = true; + } + + /** + * @param string $variableName + * @return boolean + */ + public function isGlobalVariableBlacklisted($variableName) + { + return isset($this->globalVariables[$variableName]); + } + + /** + * @param string $className + * @param string $attributeName + * @return boolean + */ + public function isStaticAttributeBlacklisted($className, $attributeName) + { + if (in_array($className, $this->classes)) { + return true; + } + + foreach ($this->classNamePrefixes as $prefix) { + if (strpos($className, $prefix) === 0) { + return true; + } + } + + $class = new ReflectionClass($className); + + foreach ($this->parentClasses as $type) { + if ($class->isSubclassOf($type)) { + return true; + } + } + + foreach ($this->interfaces as $type) { + if ($class->implementsInterface($type)) { + return true; + } + } + + if (isset($this->staticAttributes[$className][$attributeName])) { + return true; + } + + return false; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ + +namespace SebastianBergmann\GlobalState; + +/** + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/global-state + */ +class RuntimeException extends \RuntimeException implements Exception +{ +} +Text_Template + +Copyright (c) 2009-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, 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. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * A simple template engine. + * + * @since Class available since Release 1.0.0 + */ +class Text_Template +{ + /** + * @var string + */ + protected $template = ''; + + /** + * @var string + */ + protected $openDelimiter = '{'; + + /** + * @var string + */ + protected $closeDelimiter = '}'; + + /** + * @var array + */ + protected $values = array(); + + /** + * Constructor. + * + * @param string $file + * @throws InvalidArgumentException + */ + public function __construct($file = '', $openDelimiter = '{', $closeDelimiter = '}') + { + $this->setFile($file); + $this->openDelimiter = $openDelimiter; + $this->closeDelimiter = $closeDelimiter; + } + + /** + * Sets the template file. + * + * @param string $file + * @throws InvalidArgumentException + */ + public function setFile($file) + { + $distFile = $file . '.dist'; + + if (file_exists($file)) { + $this->template = file_get_contents($file); + } + + else if (file_exists($distFile)) { + $this->template = file_get_contents($distFile); + } + + else { + throw new InvalidArgumentException( + 'Template file could not be loaded.' + ); + } + } + + /** + * Sets one or more template variables. + * + * @param array $values + * @param bool $merge + */ + public function setVar(array $values, $merge = TRUE) + { + if (!$merge || empty($this->values)) { + $this->values = $values; + } else { + $this->values = array_merge($this->values, $values); + } + } + + /** + * Renders the template and returns the result. + * + * @return string + */ + public function render() + { + $keys = array(); + + foreach ($this->values as $key => $value) { + $keys[] = $this->openDelimiter . $key . $this->closeDelimiter; + } + + return str_replace($keys, $this->values, $this->template); + } + + /** + * Renders the template and writes the result to a file. + * + * @param string $target + */ + public function renderTo($target) + { + $fp = @fopen($target, 'wt'); + + if ($fp) { + fwrite($fp, $this->render()); + fclose($fp); + } else { + $error = error_get_last(); + + throw new RuntimeException( + sprintf( + 'Could not write to %s: %s', + $target, + substr( + $error['message'], + strpos($error['message'], ':') + 2 + ) + ) + ); + } + } +} + +Diff + +Copyright (c) 2002-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, 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. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Interface for implementations of longest common subsequence calculation. + * + * @package Diff + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/diff + */ +interface LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * @return array + */ + public function calculate(array $from, array $to); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Memory-efficient implementation of longest common subsequence calculation. + * + * @package Diff + * @author Sebastian Bergmann + * @author Denes Lados + * @copyright Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/diff + */ +class MemoryEfficientImplementation implements LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * @return array + */ + public function calculate(array $from, array $to) + { + $cFrom = count($from); + $cTo = count($to); + + if ($cFrom == 0) { + return array(); + } elseif ($cFrom == 1) { + if (in_array($from[0], $to)) { + return array($from[0]); + } else { + return array(); + } + } else { + $i = intval($cFrom / 2); + $fromStart = array_slice($from, 0, $i); + $fromEnd = array_slice($from, $i); + $llB = $this->length($fromStart, $to); + $llE = $this->length(array_reverse($fromEnd), array_reverse($to)); + $jMax = 0; + $max = 0; + + for ($j = 0; $j <= $cTo; $j++) { + $m = $llB[$j] + $llE[$cTo - $j]; + + if ($m >= $max) { + $max = $m; + $jMax = $j; + } + } + + $toStart = array_slice($to, 0, $jMax); + $toEnd = array_slice($to, $jMax); + + return array_merge( + $this->calculate($fromStart, $toStart), + $this->calculate($fromEnd, $toEnd) + ); + } + } + + /** + * @param array $from + * @param array $to + * @return array + */ + private function length(array $from, array $to) + { + $current = array_fill(0, count($to) + 1, 0); + $cFrom = count($from); + $cTo = count($to); + + for ($i = 0; $i < $cFrom; $i++) { + $prev = $current; + + for ($j = 0; $j < $cTo; $j++) { + if ($from[$i] == $to[$j]) { + $current[$j + 1] = $prev[$j] + 1; + } else { + $current[$j + 1] = max($current[$j], $prev[$j + 1]); + } + } + } + + return $current; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff\LCS; + +/** + * Time-efficient implementation of longest common subsequence calculation. + * + * @package Diff + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/diff + */ +class TimeEfficientImplementation implements LongestCommonSubsequence +{ + /** + * Calculates the longest common subsequence of two arrays. + * + * @param array $from + * @param array $to + * @return array + */ + public function calculate(array $from, array $to) + { + $common = array(); + $fromLength = count($from); + $toLength = count($to); + $width = $fromLength + 1; + $matrix = new \SplFixedArray($width * ($toLength + 1)); + + for ($i = 0; $i <= $fromLength; ++$i) { + $matrix[$i] = 0; + } + + for ($j = 0; $j <= $toLength; ++$j) { + $matrix[$j * $width] = 0; + } + + for ($i = 1; $i <= $fromLength; ++$i) { + for ($j = 1; $j <= $toLength; ++$j) { + $o = ($j * $width) + $i; + $matrix[$o] = max( + $matrix[$o - 1], + $matrix[$o - $width], + $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 + ); + } + } + + $i = $fromLength; + $j = $toLength; + + while ($i > 0 && $j > 0) { + if ($from[$i-1] === $to[$j-1]) { + $common[] = $from[$i-1]; + --$i; + --$j; + } else { + $o = ($j * $width) + $i; + if ($matrix[$o - $width] > $matrix[$o - 1]) { + --$j; + } else { + --$i; + } + } + } + + return array_reverse($common); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + * @package Diff + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/diff + */ +class Line +{ + const ADDED = 1; + const REMOVED = 2; + const UNCHANGED = 3; + + /** + * @var int + */ + private $type; + + /** + * @var string + */ + private $content; + + /** + * @param int $type + * @param string $content + */ + public function __construct($type = self::UNCHANGED, $content = '') + { + $this->type = $type; + $this->content = $content; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @return int + */ + public function getType() + { + return $this->type; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +use SebastianBergmann\Diff\LCS\LongestCommonSubsequence; +use SebastianBergmann\Diff\LCS\TimeEfficientImplementation; +use SebastianBergmann\Diff\LCS\MemoryEfficientImplementation; + +/** + * Diff implementation. + * + * @package Diff + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/diff + */ +class Differ +{ + /** + * @var string + */ + private $header; + + /** + * @param string $header + */ + public function __construct($header = "--- Original\n+++ New\n") + { + $this->header = $header; + } + + /** + * Returns the diff between two arrays or strings as string. + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequence $lcs + * @return string + */ + public function diff($from, $to, LongestCommonSubsequence $lcs = null) + { + if (!is_array($from) && !is_string($from)) { + $from = (string) $from; + } + + if (!is_array($to) && !is_string($to)) { + $to = (string) $to; + } + + $buffer = $this->header; + $diff = $this->diffToArray($from, $to, $lcs); + + $inOld = false; + $i = 0; + $old = array(); + + foreach ($diff as $line) { + if ($line[1] === 0 /* OLD */) { + if ($inOld === false) { + $inOld = $i; + } + } elseif ($inOld !== false) { + if (($i - $inOld) > 5) { + $old[$inOld] = $i - 1; + } + + $inOld = false; + } + + ++$i; + } + + $start = isset($old[0]) ? $old[0] : 0; + $end = count($diff); + + if ($tmp = array_search($end, $old)) { + $end = $tmp; + } + + $newChunk = true; + + for ($i = $start; $i < $end; $i++) { + if (isset($old[$i])) { + $buffer .= "\n"; + $newChunk = true; + $i = $old[$i]; + } + + if ($newChunk) { + $buffer .= "@@ @@\n"; + $newChunk = false; + } + + if ($diff[$i][1] === 1 /* ADDED */) { + $buffer .= '+' . $diff[$i][0] . "\n"; + } elseif ($diff[$i][1] === 2 /* REMOVED */) { + $buffer .= '-' . $diff[$i][0] . "\n"; + } else { + $buffer .= ' ' . $diff[$i][0] . "\n"; + } + } + + return $buffer; + } + + /** + * Returns the diff between two arrays or strings as array. + * + * Each array element contains two elements: + * - [0] => string $token + * - [1] => 2|1|0 + * + * - 2: REMOVED: $token was removed from $from + * - 1: ADDED: $token was added to $from + * - 0: OLD: $token is not changed in $to + * + * @param array|string $from + * @param array|string $to + * @param LongestCommonSubsequence $lcs + * @return array + */ + public function diffToArray($from, $to, LongestCommonSubsequence $lcs = null) + { + preg_match_all('(\r\n|\r|\n)', $from, $fromMatches); + preg_match_all('(\r\n|\r|\n)', $to, $toMatches); + + if (is_string($from)) { + $from = preg_split('(\r\n|\r|\n)', $from); + } + + if (is_string($to)) { + $to = preg_split('(\r\n|\r|\n)', $to); + } + + $start = array(); + $end = array(); + $fromLength = count($from); + $toLength = count($to); + $length = min($fromLength, $toLength); + + for ($i = 0; $i < $length; ++$i) { + if ($from[$i] === $to[$i]) { + $start[] = $from[$i]; + unset($from[$i], $to[$i]); + } else { + break; + } + } + + $length -= $i; + + for ($i = 1; $i < $length; ++$i) { + if ($from[$fromLength - $i] === $to[$toLength - $i]) { + array_unshift($end, $from[$fromLength - $i]); + unset($from[$fromLength - $i], $to[$toLength - $i]); + } else { + break; + } + } + + if ($lcs === null) { + $lcs = $this->selectLcsImplementation($from, $to); + } + + $common = $lcs->calculate(array_values($from), array_values($to)); + $diff = array(); + + if (isset($fromMatches[0]) && $toMatches[0] && + count($fromMatches[0]) === count($toMatches[0]) && + $fromMatches[0] !== $toMatches[0]) { + $diff[] = array( + '#Warning: Strings contain different line endings!', 0 + ); + } + + foreach ($start as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + reset($from); + reset($to); + + foreach ($common as $token) { + while ((($fromToken = reset($from)) !== $token)) { + $diff[] = array(array_shift($from), 2 /* REMOVED */); + } + + while ((($toToken = reset($to)) !== $token)) { + $diff[] = array(array_shift($to), 1 /* ADDED */); + } + + $diff[] = array($token, 0 /* OLD */); + + array_shift($from); + array_shift($to); + } + + while (($token = array_shift($from)) !== null) { + $diff[] = array($token, 2 /* REMOVED */); + } + + while (($token = array_shift($to)) !== null) { + $diff[] = array($token, 1 /* ADDED */); + } + + foreach ($end as $token) { + $diff[] = array($token, 0 /* OLD */); + } + + return $diff; + } + + /** + * @param array $from + * @param array $to + * @return LongestCommonSubsequence + */ + private function selectLcsImplementation(array $from, array $to) + { + // We do not want to use the time-efficient implementation if its memory + // footprint will probably exceed this value. Note that the footprint + // calculation is only an estimation for the matrix and the LCS method + // will typically allocate a bit more memory than this. + $memoryLimit = 100 * 1024 * 1024; + + if ($this->calculateEstimatedFootprint($from, $to) > $memoryLimit) { + return new MemoryEfficientImplementation; + } + + return new TimeEfficientImplementation; + } + + /** + * Calculates the estimated memory footprint for the DP-based method. + * + * @param array $from + * @param array $to + * @return integer + */ + private function calculateEstimatedFootprint(array $from, array $to) + { + $itemSize = PHP_INT_SIZE == 4 ? 76 : 144; + + return $itemSize * pow(min(count($from), count($to)), 2); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + * Unified diff parser. + * + * @package Diff + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/diff + */ +class Parser +{ + /** + * @param string $string + * @return Diff[] + */ + public function parse($string) + { + $lines = preg_split('(\r\n|\r|\n)', $string); + $lineCount = count($lines); + $diffs = array(); + $diff = null; + $collected = array(); + + for ($i = 0; $i < $lineCount; ++$i) { + if (preg_match('(^---\\s+(?P\\S+))', $lines[$i], $fromMatch) && + preg_match('(^\\+\\+\\+\\s+(?P\\S+))', $lines[$i + 1], $toMatch)) { + if ($diff !== null) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + $collected = array(); + } + + $diff = new Diff($fromMatch['file'], $toMatch['file']); + ++$i; + } else { + if (preg_match('/^(?:diff --git |index [\da-f\.]+|[+-]{3} [ab])/', $lines[$i])) { + continue; + } + $collected[] = $lines[$i]; + } + } + + if (count($collected) && ($diff !== null)) { + $this->parseFileDiff($diff, $collected); + $diffs[] = $diff; + } + + return $diffs; + } + + /** + * @param Diff $diff + * @param array $lines + */ + private function parseFileDiff(Diff $diff, array $lines) + { + $chunks = array(); + + foreach ($lines as $line) { + if (preg_match('/^@@\s+-(?P\d+)(?:,\s*(?P\d+))?\s+\+(?P\d+)(?:,\s*(?P\d+))?\s+@@/', $line, $match)) { + $chunk = new Chunk( + $match['start'], + isset($match['startrange']) ? max(1, $match['startrange']) : 1, + $match['end'], + isset($match['endrange']) ? max(1, $match['endrange']) : 1 + ); + + $chunks[] = $chunk; + $diffLines = array(); + continue; + } + + if (preg_match('/^(?P[+ -])?(?P.*)/', $line, $match)) { + $type = Line::UNCHANGED; + + if ($match['type'] == '+') { + $type = Line::ADDED; + } elseif ($match['type'] == '-') { + $type = Line::REMOVED; + } + + $diffLines[] = new Line($type, $match['line']); + + if (isset($chunk)) { + $chunk->setLines($diffLines); + } + } + } + + $diff->setChunks($chunks); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + * @package Diff + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/diff + */ +class Diff +{ + /** + * @var string + */ + private $from; + + /** + * @var string + */ + private $to; + + /** + * @var Chunk[] + */ + private $chunks; + + /** + * @param string $from + * @param string $to + * @param Chunk[] $chunks + */ + public function __construct($from, $to, array $chunks = array()) + { + $this->from = $from; + $this->to = $to; + $this->chunks = $chunks; + } + + /** + * @return string + */ + public function getFrom() + { + return $this->from; + } + + /** + * @return string + */ + public function getTo() + { + return $this->to; + } + + /** + * @return Chunk[] + */ + public function getChunks() + { + return $this->chunks; + } + + /** + * @param Chunk[] $chunks + */ + public function setChunks(array $chunks) + { + $this->chunks = $chunks; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Diff; + +/** + * @package Diff + * @author Sebastian Bergmann + * @author Kore Nordmann + * @copyright Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.github.com/sebastianbergmann/diff + */ +class Chunk +{ + /** + * @var int + */ + private $start; + + /** + * @var int + */ + private $startRange; + + /** + * @var int + */ + private $end; + /** + * @var int + */ + private $endRange; + + /** + * @var array + */ + private $lines; + + /** + * @param int $start + * @param int $startRange + * @param int $end + * @param int $endRange + * @param array $lines + */ + public function __construct($start = 0, $startRange = 1, $end = 0, $endRange = 1, array $lines = array()) + { + $this->start = (int) $start; + $this->startRange = (int) $startRange; + $this->end = (int) $end; + $this->endRange = (int) $endRange; + $this->lines = $lines; + } + + /** + * @return int + */ + public function getStart() + { + return $this->start; + } + + /** + * @return int + */ + public function getStartRange() + { + return $this->startRange; + } + + /** + * @return int + */ + public function getEnd() + { + return $this->end; + } + + /** + * @return int + */ + public function getEndRange() + { + return $this->endRange; + } + + /** + * @return array + */ + public function getLines() + { + return $this->lines; + } + + /** + * @param array $lines + */ + public function setLines(array $lines) + { + $this->lines = $lines; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Utility class for timing. + * + * @since Class available since Release 1.0.0 + */ +class PHP_Timer +{ + /** + * @var array + */ + private static $times = array( + 'hour' => 3600000, + 'minute' => 60000, + 'second' => 1000 + ); + + /** + * @var array + */ + private static $startTimes = array(); + + /** + * @var float + */ + public static $requestTime; + + /** + * Starts the timer. + */ + public static function start() + { + array_push(self::$startTimes, microtime(true)); + } + + /** + * Stops the timer and returns the elapsed time. + * + * @return float + */ + public static function stop() + { + return microtime(true) - array_pop(self::$startTimes); + } + + /** + * Formats the elapsed time as a string. + * + * @param float $time + * @return string + */ + public static function secondsToTimeString($time) + { + $ms = round($time * 1000); + + foreach (self::$times as $unit => $value) { + if ($ms >= $value) { + $time = floor($ms / $value * 100.0) / 100.0; + + return $time . ' ' . ($time == 1 ? $unit : $unit . 's'); + } + } + + return $ms . ' ms'; + } + + /** + * Formats the elapsed time since the start of the request as a string. + * + * @return string + */ + public static function timeSinceStartOfRequest() + { + return self::secondsToTimeString(microtime(true) - self::$requestTime); + } + + /** + * Returns the resources (time, memory) of the request as a string. + * + * @return string + */ + public static function resourceUsage() + { + return sprintf( + 'Time: %s, Memory: %4.2fMb', + self::timeSinceStartOfRequest(), + memory_get_peak_usage(true) / 1048576 + ); + } +} + +if (isset($_SERVER['REQUEST_TIME_FLOAT'])) { + PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME_FLOAT']; +} elseif (isset($_SERVER['REQUEST_TIME'])) { + PHP_Timer::$requestTime = $_SERVER['REQUEST_TIME']; +} else { + PHP_Timer::$requestTime = microtime(true); +} +PHP_Timer + +Copyright (c) 2010-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, 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. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +Comparator + +Copyright (c) 2002-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, 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. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use SebastianBergmann\Exporter\Exporter; + +/** + * Abstract base class for comparators which compare values for equality. + */ +abstract class Comparator +{ + /** + * @var Factory + */ + protected $factory; + + /** + * @var Exporter + */ + protected $exporter; + + public function __construct() + { + $this->exporter = new Exporter; + } + + /** + * @param Factory $factory + */ + public function setFactory(Factory $factory) + { + $this->factory = $factory; + } + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + abstract public function accepts($expected, $actual); + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + abstract public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false); +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares \SplObjectStorage instances for equality. + */ +class SplObjectStorageComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \SplObjectStorage && $actual instanceof \SplObjectStorage; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + foreach ($actual as $object) { + if (!$expected->contains($object)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + + foreach ($expected as $object) { + if (!$actual->contains($object)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares values for type equality. + */ +class TypeComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return true; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if (gettype($expected) != gettype($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + false, + sprintf( + '%s does not match expected type "%s".', + $this->exporter->shortenedExport($actual), + gettype($expected) + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares objects for equality. + */ +class ObjectComparator extends ArrayComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_object($expected) && is_object($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @param array $processed + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) + { + if (get_class($actual) !== get_class($expected)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + sprintf( + '%s is not instance of expected class "%s".', + $this->exporter->export($actual), + get_class($expected) + ) + ); + } + + // don't compare twice to allow for cyclic dependencies + if (in_array(array($actual, $expected), $processed, true) || + in_array(array($expected, $actual), $processed, true)) { + return; + } + + $processed[] = array($actual, $expected); + + // don't compare objects if they are identical + // this helps to avoid the error "maximum function nesting level reached" + // CAUTION: this conditional clause is not tested + if ($actual !== $expected) { + try { + parent::assertEquals( + $this->toArray($expected), + $this->toArray($actual), + $delta, + $canonicalize, + $ignoreCase, + $processed + ); + } catch (ComparisonFailure $e) { + throw new ComparisonFailure( + $expected, + $actual, + // replace "Array" with "MyClass object" + substr_replace($e->getExpectedAsString(), get_class($expected) . ' Object', 0, 5), + substr_replace($e->getActualAsString(), get_class($actual) . ' Object', 0, 5), + false, + 'Failed asserting that two objects are equal.' + ); + } + } + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + return $this->exporter->toArray($object); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares resources for equality. + */ +class ResourceComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_resource($expected) && is_resource($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($actual != $expected) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares arrays for equality. + */ +class ArrayComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return is_array($expected) && is_array($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @param array $processed + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false, array &$processed = array()) + { + if ($canonicalize) { + sort($expected); + sort($actual); + } + + $remaining = $actual; + $expString = $actString = "Array (\n"; + $equal = true; + + foreach ($expected as $key => $value) { + unset($remaining[$key]); + + if (!array_key_exists($key, $actual)) { + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $equal = false; + + continue; + } + + try { + $comparator = $this->factory->getComparatorFor($value, $actual[$key]); + $comparator->assertEquals($value, $actual[$key], $delta, $canonicalize, $ignoreCase, $processed); + + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($actual[$key]) + ); + } catch (ComparisonFailure $e) { + $expString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $e->getExpectedAsString() + ? $this->indent($e->getExpectedAsString()) + : $this->exporter->shortenedExport($e->getExpected()) + ); + + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $e->getActualAsString() + ? $this->indent($e->getActualAsString()) + : $this->exporter->shortenedExport($e->getActual()) + ); + + $equal = false; + } + } + + foreach ($remaining as $key => $value) { + $actString .= sprintf( + " %s => %s\n", + $this->exporter->export($key), + $this->exporter->shortenedExport($value) + ); + + $equal = false; + } + + $expString .= ')'; + $actString .= ')'; + + if (!$equal) { + throw new ComparisonFailure( + $expected, + $actual, + $expString, + $actString, + false, + 'Failed asserting that two arrays are equal.' + ); + } + } + + protected function indent($lines) + { + return trim(str_replace("\n", "\n ", $lines)); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares DateTimeInterface instances for equality. + */ +class DateTimeComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return ($expected instanceof \DateTime || $expected instanceof \DateTimeInterface) && + ($actual instanceof \DateTime || $actual instanceof \DateTimeInterface); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $delta = new \DateInterval(sprintf('PT%sS', abs($delta))); + + $expectedLower = clone $expected; + $expectedUpper = clone $expected; + + if ($actual < $expectedLower->sub($delta) || + $actual > $expectedUpper->add($delta)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->dateTimeToString($expected), + $this->dateTimeToString($actual), + false, + 'Failed asserting that two DateTime objects are equal.' + ); + } + } + + /** + * Returns an ISO 8601 formatted string representation of a datetime or + * 'Invalid DateTimeInterface object' if the provided DateTimeInterface was not properly + * initialized. + * + * @param \DateTimeInterface $datetime + * @return string + */ + protected function dateTimeToString($datetime) + { + $string = $datetime->format(\DateTime::ISO8601); + + return $string ? $string : 'Invalid DateTimeInterface object'; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares Exception instances for equality. + */ +class ExceptionComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \Exception && $actual instanceof \Exception; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset( + $array['file'], + $array['line'], + $array['trace'], + $array['string'], + $array['xdebug_message'] + ); + + return $array; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use DOMDocument; +use DOMNode; + +/** + * Compares DOMNode instances for equality. + */ +class DOMNodeComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof DOMNode && $actual instanceof DOMNode; + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $expectedAsString = $this->nodeToText($expected, true, $ignoreCase); + $actualAsString = $this->nodeToText($actual, true, $ignoreCase); + + if ($expectedAsString !== $actualAsString) { + if ($expected instanceof DOMDocument) { + $type = 'documents'; + } else { + $type = 'nodes'; + } + + throw new ComparisonFailure( + $expected, + $actual, + $expectedAsString, + $actualAsString, + false, + sprintf("Failed asserting that two DOM %s are equal.\n", $type) + ); + } + } + + /** + * Returns the normalized, whitespace-cleaned, and indented textual + * representation of a DOMNode. + * + * @param DOMNode $node + * @param bool $canonicalize + * @param bool $ignoreCase + * @return string + */ + private function nodeToText(DOMNode $node, $canonicalize, $ignoreCase) + { + if ($canonicalize) { + $document = new DOMDocument; + $document->loadXML($node->C14N()); + + $node = $document; + } + + if ($node instanceof DOMDocument) { + $document = $node; + } else { + $document = $node->ownerDocument; + } + + $document->formatOutput = true; + $document->normalizeDocument(); + + if ($node instanceof DOMDocument) { + $text = $node->saveXML(); + } else { + $text = $document->saveXML($node); + } + + if ($ignoreCase) { + $text = strtolower($text); + } + + return $text; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares scalar or NULL values for equality. + */ +class ScalarComparator extends Comparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + * @since Method available since Release 3.6.0 + */ + public function accepts($expected, $actual) + { + return ((is_scalar($expected) xor null === $expected) && + (is_scalar($actual) xor null === $actual)) + // allow comparison between strings and objects featuring __toString() + || (is_string($expected) && is_object($actual) && method_exists($actual, '__toString')) + || (is_object($expected) && method_exists($expected, '__toString') && is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + $expectedToCompare = $expected; + $actualToCompare = $actual; + + // always compare as strings to avoid strange behaviour + // otherwise 0 == 'Foobar' + if (is_string($expected) || is_string($actual)) { + $expectedToCompare = (string) $expectedToCompare; + $actualToCompare = (string) $actualToCompare; + + if ($ignoreCase) { + $expectedToCompare = strtolower($expectedToCompare); + $actualToCompare = strtolower($actualToCompare); + } + } + + if ($expectedToCompare != $actualToCompare) { + if (is_string($expected) && is_string($actual)) { + throw new ComparisonFailure( + $expected, + $actual, + $this->exporter->export($expected), + $this->exporter->export($actual), + false, + 'Failed asserting that two strings are equal.' + ); + } + + throw new ComparisonFailure( + $expected, + $actual, + // no diff is required + '', + '', + false, + sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares PHPUnit_Framework_MockObject_MockObject instances for equality. + */ +class MockObjectComparator extends ObjectComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return $expected instanceof \PHPUnit_Framework_MockObject_MockObject && $actual instanceof \PHPUnit_Framework_MockObject_MockObject; + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * @return array + */ + protected function toArray($object) + { + $array = parent::toArray($object); + + unset($array['__phpunit_invocationMocker']); + + return $array; + } +} + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares numerical values for equality. + */ +class NumericComparator extends ScalarComparator +{ + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + // all numerical values, but not if one of them is a double + // or both of them are strings + return is_numeric($expected) && is_numeric($actual) && + !(is_double($expected) || is_double($actual)) && + !(is_string($expected) && is_string($actual)); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if (is_infinite($actual) && is_infinite($expected)) { + return; + } + + if ((is_infinite($actual) xor is_infinite($expected)) || + (is_nan($actual) or is_nan($expected)) || + abs($actual - $expected) > $delta) { + throw new ComparisonFailure( + $expected, + $actual, + '', + '', + false, + sprintf( + 'Failed asserting that %s matches expected %s.', + $this->exporter->export($actual), + $this->exporter->export($expected) + ) + ); + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Factory for comparators which compare values for equality. + */ +class Factory +{ + /** + * @var Comparator[] + */ + private $comparators = array(); + + /** + * @var Factory + */ + private static $instance; + + /** + * Constructs a new factory. + */ + public function __construct() + { + $this->register(new TypeComparator); + $this->register(new ScalarComparator); + $this->register(new NumericComparator); + $this->register(new DoubleComparator); + $this->register(new ArrayComparator); + $this->register(new ResourceComparator); + $this->register(new ObjectComparator); + $this->register(new ExceptionComparator); + $this->register(new SplObjectStorageComparator); + $this->register(new DOMNodeComparator); + $this->register(new MockObjectComparator); + $this->register(new DateTimeComparator); + } + + /** + * @return Factory + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new self; + } + + return self::$instance; + } + + /** + * Returns the correct comparator for comparing two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return Comparator + */ + public function getComparatorFor($expected, $actual) + { + foreach ($this->comparators as $comparator) { + if ($comparator->accepts($expected, $actual)) { + return $comparator; + } + } + } + + /** + * Registers a new comparator. + * + * This comparator will be returned by getInstance() if its accept() method + * returns TRUE for the compared values. It has higher priority than the + * existing comparators, meaning that its accept() method will be tested + * before those of the other comparators. + * + * @param Comparator $comparator The registered comparator + */ + public function register(Comparator $comparator) + { + array_unshift($this->comparators, $comparator); + + $comparator->setFactory($this); + } + + /** + * Unregisters a comparator. + * + * This comparator will no longer be returned by getInstance(). + * + * @param Comparator $comparator The unregistered comparator + */ + public function unregister(Comparator $comparator) + { + foreach ($this->comparators as $key => $_comparator) { + if ($comparator === $_comparator) { + unset($this->comparators[$key]); + } + } + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +/** + * Compares doubles for equality. + */ +class DoubleComparator extends NumericComparator +{ + /** + * Smallest value available in PHP. + * + * @var float + */ + const EPSILON = 0.0000000001; + + /** + * Returns whether the comparator can compare two values. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @return bool + */ + public function accepts($expected, $actual) + { + return (is_double($expected) || is_double($actual)) && is_numeric($expected) && is_numeric($actual); + } + + /** + * Asserts that two values are equal. + * + * @param mixed $expected The first value to compare + * @param mixed $actual The second value to compare + * @param float $delta The allowed numerical distance between two values to + * consider them equal + * @param bool $canonicalize If set to TRUE, arrays are sorted before + * comparison + * @param bool $ignoreCase If set to TRUE, upper- and lowercasing is + * ignored when comparing string values + * @throws ComparisonFailure Thrown when the comparison + * fails. Contains information about the + * specific errors that lead to the failure. + */ + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + if ($delta == 0) { + $delta = self::EPSILON; + } + + parent::assertEquals($expected, $actual, $delta, $canonicalize, $ignoreCase); + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Comparator; + +use SebastianBergmann\Diff\Differ; + +/** + * Thrown when an assertion for string equality failed. + */ +class ComparisonFailure extends \RuntimeException +{ + /** + * Expected value of the retrieval which does not match $actual. + * @var mixed + */ + protected $expected; + + /** + * Actually retrieved value which does not match $expected. + * @var mixed + */ + protected $actual; + + /** + * The string representation of the expected value + * @var string + */ + protected $expectedAsString; + + /** + * The string representation of the actual value + * @var string + */ + protected $actualAsString; + + /** + * @var bool + */ + protected $identical; + + /** + * Optional message which is placed in front of the first line + * returned by toString(). + * @var string + */ + protected $message; + + /** + * Initialises with the expected value and the actual value. + * + * @param mixed $expected Expected value retrieved. + * @param mixed $actual Actual value retrieved. + * @param string $expectedAsString + * @param string $actualAsString + * @param bool $identical + * @param string $message A string which is prefixed on all returned lines + * in the difference output. + */ + public function __construct($expected, $actual, $expectedAsString, $actualAsString, $identical = false, $message = '') + { + $this->expected = $expected; + $this->actual = $actual; + $this->expectedAsString = $expectedAsString; + $this->actualAsString = $actualAsString; + $this->message = $message; + } + + /** + * @return mixed + */ + public function getActual() + { + return $this->actual; + } + + /** + * @return mixed + */ + public function getExpected() + { + return $this->expected; + } + + /** + * @return string + */ + public function getActualAsString() + { + return $this->actualAsString; + } + + /** + * @return string + */ + public function getExpectedAsString() + { + return $this->expectedAsString; + } + + /** + * @return string + */ + public function getDiff() + { + if (!$this->actualAsString && !$this->expectedAsString) { + return ''; + } + + $differ = new Differ("\n--- Expected\n+++ Actual\n"); + + return $differ->diff($this->expectedAsString, $this->actualAsString); + } + + /** + * @return string + */ + public function toString() + { + return $this->message . $this->getDiff(); + } +} +Copyright (c) 2013 Konstantin Kudryashov + Marcello Duarte + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Prophecies revealer interface. + * + * @author Konstantin Kudryashov + */ +interface RevealerInterface +{ + /** + * Unwraps value(s). + * + * @param mixed $value + * + * @return mixed + */ + public function reveal($value); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Controllable doubles interface. + * + * @author Konstantin Kudryashov + */ +interface ProphecySubjectInterface +{ + /** + * Sets subject prophecy. + * + * @param ProphecyInterface $prophecy + */ + public function setProphecy(ProphecyInterface $prophecy); + + /** + * Returns subject prophecy. + * + * @return ProphecyInterface + */ + public function getProphecy(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Basic prophecies revealer. + * + * @author Konstantin Kudryashov + */ +class Revealer implements RevealerInterface +{ + /** + * Unwraps value(s). + * + * @param mixed $value + * + * @return mixed + */ + public function reveal($value) + { + if (is_array($value)) { + return array_map(array($this, __FUNCTION__), $value); + } + + if (!is_object($value)) { + return $value; + } + + if ($value instanceof ProphecyInterface) { + $value = $value->reveal(); + } + + return $value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +/** + * Core Prophecy interface. + * + * @author Konstantin Kudryashov + */ +interface ProphecyInterface +{ + /** + * Reveals prophecy object (double) . + * + * @return object + */ + public function reveal(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Call\Call; +use Prophecy\Doubler\LazyDouble; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Call\CallCenter; +use Prophecy\Exception\Prophecy\ObjectProphecyException; +use Prophecy\Exception\Prophecy\MethodProphecyException; +use Prophecy\Exception\Prediction\AggregateException; +use Prophecy\Exception\Prediction\PredictionException; + +/** + * Object prophecy. + * + * @author Konstantin Kudryashov + */ +class ObjectProphecy implements ProphecyInterface +{ + private $lazyDouble; + private $callCenter; + private $revealer; + private $comparatorFactory; + + /** + * @var MethodProphecy[][] + */ + private $methodProphecies = array(); + + /** + * Initializes object prophecy. + * + * @param LazyDouble $lazyDouble + * @param CallCenter $callCenter + * @param RevealerInterface $revealer + * @param ComparatorFactory $comparatorFactory + */ + public function __construct( + LazyDouble $lazyDouble, + CallCenter $callCenter = null, + RevealerInterface $revealer = null, + ComparatorFactory $comparatorFactory = null + ) { + $this->lazyDouble = $lazyDouble; + $this->callCenter = $callCenter ?: new CallCenter; + $this->revealer = $revealer ?: new Revealer; + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Forces double to extend specific class. + * + * @param string $class + * + * @return $this + */ + public function willExtend($class) + { + $this->lazyDouble->setParentClass($class); + + return $this; + } + + /** + * Forces double to implement specific interface. + * + * @param string $interface + * + * @return $this + */ + public function willImplement($interface) + { + $this->lazyDouble->addInterface($interface); + + return $this; + } + + /** + * Sets constructor arguments. + * + * @param array $arguments + * + * @return $this + */ + public function willBeConstructedWith(array $arguments = null) + { + $this->lazyDouble->setArguments($arguments); + + return $this; + } + + /** + * Reveals double. + * + * @return object + * + * @throws \Prophecy\Exception\Prophecy\ObjectProphecyException If double doesn't implement needed interface + */ + public function reveal() + { + $double = $this->lazyDouble->getInstance(); + + if (null === $double || !$double instanceof ProphecySubjectInterface) { + throw new ObjectProphecyException( + "Generated double must implement ProphecySubjectInterface, but it does not.\n". + 'It seems you have wrongly configured doubler without required ClassPatch.', + $this + ); + } + + $double->setProphecy($this); + + return $double; + } + + /** + * Adds method prophecy to object prophecy. + * + * @param MethodProphecy $methodProphecy + * + * @throws \Prophecy\Exception\Prophecy\MethodProphecyException If method prophecy doesn't + * have arguments wildcard + */ + public function addMethodProphecy(MethodProphecy $methodProphecy) + { + $argumentsWildcard = $methodProphecy->getArgumentsWildcard(); + if (null === $argumentsWildcard) { + throw new MethodProphecyException(sprintf( + "Can not add prophecy for a method `%s::%s()`\n". + "as you did not specify arguments wildcard for it.", + get_class($this->reveal()), + $methodProphecy->getMethodName() + ), $methodProphecy); + } + + $methodName = $methodProphecy->getMethodName(); + + if (!isset($this->methodProphecies[$methodName])) { + $this->methodProphecies[$methodName] = array(); + } + + $this->methodProphecies[$methodName][] = $methodProphecy; + } + + /** + * Returns either all or related to single method prophecies. + * + * @param null|string $methodName + * + * @return MethodProphecy[] + */ + public function getMethodProphecies($methodName = null) + { + if (null === $methodName) { + return $this->methodProphecies; + } + + if (!isset($this->methodProphecies[$methodName])) { + return array(); + } + + return $this->methodProphecies[$methodName]; + } + + /** + * Makes specific method call. + * + * @param string $methodName + * @param array $arguments + * + * @return mixed + */ + public function makeProphecyMethodCall($methodName, array $arguments) + { + $arguments = $this->revealer->reveal($arguments); + $return = $this->callCenter->makeCall($this, $methodName, $arguments); + + return $this->revealer->reveal($return); + } + + /** + * Finds calls by method name & arguments wildcard. + * + * @param string $methodName + * @param ArgumentsWildcard $wildcard + * + * @return Call[] + */ + public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard) + { + return $this->callCenter->findCalls($methodName, $wildcard); + } + + /** + * Checks that registered method predictions do not fail. + * + * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail + */ + public function checkProphecyMethodsPredictions() + { + $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal()))); + $exception->setObjectProphecy($this); + + foreach ($this->methodProphecies as $prophecies) { + foreach ($prophecies as $prophecy) { + try { + $prophecy->checkPrediction(); + } catch (PredictionException $e) { + $exception->append($e); + } + } + } + + if (count($exception->getExceptions())) { + throw $exception; + } + } + + /** + * Creates new method prophecy using specified method name and arguments. + * + * @param string $methodName + * @param array $arguments + * + * @return MethodProphecy + */ + public function __call($methodName, array $arguments) + { + $arguments = new ArgumentsWildcard($this->revealer->reveal($arguments)); + + foreach ($this->getMethodProphecies($methodName) as $prophecy) { + $argumentsWildcard = $prophecy->getArgumentsWildcard(); + $comparator = $this->comparatorFactory->getComparatorFor( + $argumentsWildcard, $arguments + ); + + try { + $comparator->assertEquals($argumentsWildcard, $arguments); + return $prophecy; + } catch (ComparisonFailure $failure) {} + } + + return new MethodProphecy($this, $methodName, $arguments); + } + + /** + * Tries to get property value from double. + * + * @param string $name + */ + public function __get($name) + { + return $this->reveal()->$name; + } + + /** + * Tries to set property value to double. + * + * @param string $name + * @param string $value + */ + public function __set($name, $value) + { + $this->reveal()->$name = $this->revealer->reveal($value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prophecy; + +use Prophecy\Argument; +use Prophecy\Prophet; +use Prophecy\Promise; +use Prophecy\Prediction; +use Prophecy\Exception\Doubler\MethodNotFoundException; +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Prophecy\MethodProphecyException; + +/** + * Method prophecy. + * + * @author Konstantin Kudryashov + */ +class MethodProphecy +{ + private $objectProphecy; + private $methodName; + private $argumentsWildcard; + private $promise; + private $prediction; + private $checkedPredictions = array(); + private $bound = false; + + /** + * Initializes method prophecy. + * + * @param ObjectProphecy $objectProphecy + * @param string $methodName + * @param null|Argument\ArgumentsWildcard|array $arguments + * + * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found + */ + public function __construct(ObjectProphecy $objectProphecy, $methodName, $arguments = null) + { + $double = $objectProphecy->reveal(); + if (!method_exists($double, $methodName)) { + throw new MethodNotFoundException(sprintf( + 'Method `%s::%s()` is not defined.', get_class($double), $methodName + ), get_class($double), $methodName, $arguments); + } + + $this->objectProphecy = $objectProphecy; + $this->methodName = $methodName; + + $reflectedMethod = new \ReflectionMethod($double, $methodName); + if ($reflectedMethod->isFinal()) { + throw new MethodProphecyException(sprintf( + "Can not add prophecy for a method `%s::%s()`\n". + "as it is a final method.", + get_class($double), + $methodName + ), $this); + } + + if (null !== $arguments) { + $this->withArguments($arguments); + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) { + $type = (string) $reflectedMethod->getReturnType(); + $this->will(function () use ($type) { + switch ($type) { + case 'string': return ''; + case 'float': return 0.0; + case 'int': return 0; + case 'bool': return false; + case 'array': return array(); + + case 'callable': + case 'Closure': + return function () {}; + + case 'Traversable': + case 'Generator': + // Remove eval() when minimum version >=5.5 + $generator = eval('return function () { yield; };'); + return $generator(); + + default: + $prophet = new Prophet; + return $prophet->prophesize($type)->reveal(); + } + }); + } + } + + /** + * Sets argument wildcard. + * + * @param array|Argument\ArgumentsWildcard $arguments + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function withArguments($arguments) + { + if (is_array($arguments)) { + $arguments = new Argument\ArgumentsWildcard($arguments); + } + + if (!$arguments instanceof Argument\ArgumentsWildcard) { + throw new InvalidArgumentException(sprintf( + "Either an array or an instance of ArgumentsWildcard expected as\n". + 'a `MethodProphecy::withArguments()` argument, but got %s.', + gettype($arguments) + )); + } + + $this->argumentsWildcard = $arguments; + + return $this; + } + + /** + * Sets custom promise to the prophecy. + * + * @param callable|Promise\PromiseInterface $promise + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function will($promise) + { + if (is_callable($promise)) { + $promise = new Promise\CallbackPromise($promise); + } + + if (!$promise instanceof Promise\PromiseInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PromiseInterface, but got %s.', + gettype($promise) + )); + } + + $this->bindToObjectProphecy(); + $this->promise = $promise; + + return $this; + } + + /** + * Sets return promise to the prophecy. + * + * @see Prophecy\Promise\ReturnPromise + * + * @return $this + */ + public function willReturn() + { + return $this->will(new Promise\ReturnPromise(func_get_args())); + } + + /** + * Sets return argument promise to the prophecy. + * + * @param int $index The zero-indexed number of the argument to return + * + * @see Prophecy\Promise\ReturnArgumentPromise + * + * @return $this + */ + public function willReturnArgument($index = 0) + { + return $this->will(new Promise\ReturnArgumentPromise($index)); + } + + /** + * Sets throw promise to the prophecy. + * + * @see Prophecy\Promise\ThrowPromise + * + * @param string|\Exception $exception Exception class or instance + * + * @return $this + */ + public function willThrow($exception) + { + return $this->will(new Promise\ThrowPromise($exception)); + } + + /** + * Sets custom prediction to the prophecy. + * + * @param callable|Prediction\PredictionInterface $prediction + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function should($prediction) + { + if (is_callable($prediction)) { + $prediction = new Prediction\CallbackPrediction($prediction); + } + + if (!$prediction instanceof Prediction\PredictionInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PredictionInterface, but got %s.', + gettype($prediction) + )); + } + + $this->bindToObjectProphecy(); + $this->prediction = $prediction; + + return $this; + } + + /** + * Sets call prediction to the prophecy. + * + * @see Prophecy\Prediction\CallPrediction + * + * @return $this + */ + public function shouldBeCalled() + { + return $this->should(new Prediction\CallPrediction); + } + + /** + * Sets no calls prediction to the prophecy. + * + * @see Prophecy\Prediction\NoCallsPrediction + * + * @return $this + */ + public function shouldNotBeCalled() + { + return $this->should(new Prediction\NoCallsPrediction); + } + + /** + * Sets call times prediction to the prophecy. + * + * @see Prophecy\Prediction\CallTimesPrediction + * + * @param $count + * + * @return $this + */ + public function shouldBeCalledTimes($count) + { + return $this->should(new Prediction\CallTimesPrediction($count)); + } + + /** + * Checks provided prediction immediately. + * + * @param callable|Prediction\PredictionInterface $prediction + * + * @return $this + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function shouldHave($prediction) + { + if (is_callable($prediction)) { + $prediction = new Prediction\CallbackPrediction($prediction); + } + + if (!$prediction instanceof Prediction\PredictionInterface) { + throw new InvalidArgumentException(sprintf( + 'Expected callable or instance of PredictionInterface, but got %s.', + gettype($prediction) + )); + } + + if (null === $this->promise) { + $this->willReturn(); + } + + $calls = $this->getObjectProphecy()->findProphecyMethodCalls( + $this->getMethodName(), + $this->getArgumentsWildcard() + ); + + try { + $prediction->check($calls, $this->getObjectProphecy(), $this); + $this->checkedPredictions[] = $prediction; + } catch (\Exception $e) { + $this->checkedPredictions[] = $prediction; + + throw $e; + } + + return $this; + } + + /** + * Checks call prediction. + * + * @see Prophecy\Prediction\CallPrediction + * + * @return $this + */ + public function shouldHaveBeenCalled() + { + return $this->shouldHave(new Prediction\CallPrediction); + } + + /** + * Checks no calls prediction. + * + * @see Prophecy\Prediction\NoCallsPrediction + * + * @return $this + */ + public function shouldNotHaveBeenCalled() + { + return $this->shouldHave(new Prediction\NoCallsPrediction); + } + + /** + * Checks no calls prediction. + * + * @see Prophecy\Prediction\NoCallsPrediction + * @deprecated + * + * @return $this + */ + public function shouldNotBeenCalled() + { + return $this->shouldNotHaveBeenCalled(); + } + + /** + * Checks call times prediction. + * + * @see Prophecy\Prediction\CallTimesPrediction + * + * @param int $count + * + * @return $this + */ + public function shouldHaveBeenCalledTimes($count) + { + return $this->shouldHave(new Prediction\CallTimesPrediction($count)); + } + + /** + * Checks currently registered [with should(...)] prediction. + */ + public function checkPrediction() + { + if (null === $this->prediction) { + return; + } + + $this->shouldHave($this->prediction); + } + + /** + * Returns currently registered promise. + * + * @return null|Promise\PromiseInterface + */ + public function getPromise() + { + return $this->promise; + } + + /** + * Returns currently registered prediction. + * + * @return null|Prediction\PredictionInterface + */ + public function getPrediction() + { + return $this->prediction; + } + + /** + * Returns predictions that were checked on this object. + * + * @return Prediction\PredictionInterface[] + */ + public function getCheckedPredictions() + { + return $this->checkedPredictions; + } + + /** + * Returns object prophecy this method prophecy is tied to. + * + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } + + /** + * Returns method name. + * + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * Returns arguments wildcard. + * + * @return Argument\ArgumentsWildcard + */ + public function getArgumentsWildcard() + { + return $this->argumentsWildcard; + } + + private function bindToObjectProphecy() + { + if ($this->bound) { + return; + } + + $this->getObjectProphecy()->addMethodProphecy($this); + $this->bound = true; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Comparator; + +use SebastianBergmann\Comparator\Comparator; +use SebastianBergmann\Comparator\ComparisonFailure; + +/** + * Closure comparator. + * + * @author Konstantin Kudryashov + */ +final class ClosureComparator extends Comparator +{ + public function accepts($expected, $actual) + { + return is_object($expected) && $expected instanceof \Closure + && is_object($actual) && $actual instanceof \Closure; + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false) + { + throw new ComparisonFailure( + $expected, + $actual, + // we don't need a diff + '', + '', + false, + 'all closures are born different' + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Comparator; + +use SebastianBergmann\Comparator\Factory as BaseFactory; + +/** + * Prophecy comparator factory. + * + * @author Konstantin Kudryashov + */ +final class Factory extends BaseFactory +{ + /** + * @var Factory + */ + private static $instance; + + public function __construct() + { + parent::__construct(); + + $this->register(new ClosureComparator()); + } + + /** + * @return Factory + */ + public static function getInstance() + { + if (self::$instance === null) { + self::$instance = new Factory; + } + + return self::$instance; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy; + +use Prophecy\Argument\Token; + +/** + * Argument tokens shortcuts. + * + * @author Konstantin Kudryashov + */ +class Argument +{ + /** + * Checks that argument is exact value or object. + * + * @param mixed $value + * + * @return Token\ExactValueToken + */ + public static function exact($value) + { + return new Token\ExactValueToken($value); + } + + /** + * Checks that argument is of specific type or instance of specific class. + * + * @param string $type Type name (`integer`, `string`) or full class name + * + * @return Token\TypeToken + */ + public static function type($type) + { + return new Token\TypeToken($type); + } + + /** + * Checks that argument object has specific state. + * + * @param string $methodName + * @param mixed $value + * + * @return Token\ObjectStateToken + */ + public static function which($methodName, $value) + { + return new Token\ObjectStateToken($methodName, $value); + } + + /** + * Checks that argument matches provided callback. + * + * @param callable $callback + * + * @return Token\CallbackToken + */ + public static function that($callback) + { + return new Token\CallbackToken($callback); + } + + /** + * Matches any single value. + * + * @return Token\AnyValueToken + */ + public static function any() + { + return new Token\AnyValueToken; + } + + /** + * Matches all values to the rest of the signature. + * + * @return Token\AnyValuesToken + */ + public static function cetera() + { + return new Token\AnyValuesToken; + } + + /** + * Checks that argument matches all tokens + * + * @param mixed ... a list of tokens + * + * @return Token\LogicalAndToken + */ + public static function allOf() + { + return new Token\LogicalAndToken(func_get_args()); + } + + /** + * Checks that argument array or countable object has exact number of elements. + * + * @param integer $value array elements count + * + * @return Token\ArrayCountToken + */ + public static function size($value) + { + return new Token\ArrayCountToken($value); + } + + /** + * Checks that argument array contains (key, value) pair + * + * @param mixed $key exact value or token + * @param mixed $value exact value or token + * + * @return Token\ArrayEntryToken + */ + public static function withEntry($key, $value) + { + return new Token\ArrayEntryToken($key, $value); + } + + /** + * Checks that arguments array entries all match value + * + * @param mixed $value + * + * @return Token\ArrayEveryEntryToken + */ + public static function withEveryEntry($value) + { + return new Token\ArrayEveryEntryToken($value); + } + + /** + * Checks that argument array contains value + * + * @param mixed $value + * + * @return Token\ArrayEntryToken + */ + public static function containing($value) + { + return new Token\ArrayEntryToken(self::any(), $value); + } + + /** + * Checks that argument array has key + * + * @param mixed $key exact value or token + * + * @return Token\ArrayEntryToken + */ + public static function withKey($key) + { + return new Token\ArrayEntryToken($key, self::any()); + } + + /** + * Checks that argument does not match the value|token. + * + * @param mixed $value either exact value or argument token + * + * @return Token\LogicalNotToken + */ + public static function not($value) + { + return new Token\LogicalNotToken($value); + } + + /** + * @param string $value + * + * @return Token\StringContainsToken + */ + public static function containingString($value) + { + return new Token\StringContainsToken($value); + } + + /** + * Checks that argument is identical value. + * + * @param mixed $value + * + * @return Token\IdenticalValueToken + */ + public static function is($value) + { + return new Token\IdenticalValueToken($value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy; + +use Prophecy\Doubler\Doubler; +use Prophecy\Doubler\LazyDouble; +use Prophecy\Doubler\ClassPatch; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\RevealerInterface; +use Prophecy\Prophecy\Revealer; +use Prophecy\Call\CallCenter; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\PredictionException; +use Prophecy\Exception\Prediction\AggregateException; + +/** + * Prophet creates prophecies. + * + * @author Konstantin Kudryashov + */ +class Prophet +{ + private $doubler; + private $revealer; + private $util; + + /** + * @var ObjectProphecy[] + */ + private $prophecies = array(); + + /** + * Initializes Prophet. + * + * @param null|Doubler $doubler + * @param null|RevealerInterface $revealer + * @param null|StringUtil $util + */ + public function __construct(Doubler $doubler = null, RevealerInterface $revealer = null, + StringUtil $util = null) + { + if (null === $doubler) { + $doubler = new Doubler; + $doubler->registerClassPatch(new ClassPatch\SplFileInfoPatch); + $doubler->registerClassPatch(new ClassPatch\TraversablePatch); + $doubler->registerClassPatch(new ClassPatch\DisableConstructorPatch); + $doubler->registerClassPatch(new ClassPatch\ProphecySubjectPatch); + $doubler->registerClassPatch(new ClassPatch\ReflectionClassNewInstancePatch); + $doubler->registerClassPatch(new ClassPatch\HhvmExceptionPatch()); + $doubler->registerClassPatch(new ClassPatch\MagicCallPatch); + $doubler->registerClassPatch(new ClassPatch\KeywordPatch); + } + + $this->doubler = $doubler; + $this->revealer = $revealer ?: new Revealer; + $this->util = $util ?: new StringUtil; + } + + /** + * Creates new object prophecy. + * + * @param null|string $classOrInterface Class or interface name + * + * @return ObjectProphecy + */ + public function prophesize($classOrInterface = null) + { + $this->prophecies[] = $prophecy = new ObjectProphecy( + new LazyDouble($this->doubler), + new CallCenter($this->util), + $this->revealer + ); + + if ($classOrInterface && class_exists($classOrInterface)) { + return $prophecy->willExtend($classOrInterface); + } + + if ($classOrInterface && interface_exists($classOrInterface)) { + return $prophecy->willImplement($classOrInterface); + } + + return $prophecy; + } + + /** + * Returns all created object prophecies. + * + * @return ObjectProphecy[] + */ + public function getProphecies() + { + return $this->prophecies; + } + + /** + * Returns Doubler instance assigned to this Prophet. + * + * @return Doubler + */ + public function getDoubler() + { + return $this->doubler; + } + + /** + * Checks all predictions defined by prophecies of this Prophet. + * + * @throws Exception\Prediction\AggregateException If any prediction fails + */ + public function checkPredictions() + { + $exception = new AggregateException("Some predictions failed:\n"); + foreach ($this->prophecies as $prophecy) { + try { + $prophecy->checkProphecyMethodsPredictions(); + } catch (PredictionException $e) { + $exception->append($e); + } + } + + if (count($exception->getExceptions())) { + throw $exception; + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception; + +/** + * Core Prophecy exception interface. + * All Prophecy exceptions implement it. + * + * @author Konstantin Kudryashov + */ +interface Exception +{ + /** + * @return string + */ + public function getMessage(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Prophecy\ObjectProphecy; + +class ObjectProphecyException extends \RuntimeException implements ProphecyException +{ + private $objectProphecy; + + public function __construct($message, ObjectProphecy $objectProphecy) + { + parent::__construct($message); + + $this->objectProphecy = $objectProphecy; + } + + /** + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Exception\Exception; + +interface ProphecyException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prophecy; + +use Prophecy\Prophecy\MethodProphecy; + +class MethodProphecyException extends ObjectProphecyException +{ + private $methodProphecy; + + public function __construct($message, MethodProphecy $methodProphecy) + { + parent::__construct($message, $methodProphecy->getObjectProphecy()); + + $this->methodProphecy = $methodProphecy; + } + + /** + * @return MethodProphecy + */ + public function getMethodProphecy() + { + return $this->methodProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class InterfaceNotFoundException extends ClassNotFoundException +{ + public function getInterfaceName() + { + return $this->getClassname(); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use ReflectionClass; + +class ClassMirrorException extends \RuntimeException implements DoublerException +{ + private $class; + + public function __construct($message, ReflectionClass $class) + { + parent::__construct($message); + + $this->class = $class; + } + + public function getReflectedClass() + { + return $this->class; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use RuntimeException; + +class DoubleException extends RuntimeException implements DoublerException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use Prophecy\Exception\Exception; + +interface DoublerException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class MethodNotFoundException extends DoubleException +{ + /** + * @var string + */ + private $classname; + + /** + * @var string + */ + private $methodName; + + /** + * @var array + */ + private $arguments; + + /** + * @param string $message + * @param string $classname + * @param string $methodName + * @param null|Argument\ArgumentsWildcard|array $arguments + */ + public function __construct($message, $classname, $methodName, $arguments = null) + { + parent::__construct($message); + + $this->classname = $classname; + $this->methodName = $methodName; + $this->arguments = $arguments; + } + + public function getClassname() + { + return $this->classname; + } + + public function getMethodName() + { + return $this->methodName; + } + + public function getArguments() + { + return $this->arguments; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class ReturnByReferenceException extends DoubleException +{ + private $classname; + private $methodName; + + /** + * @param string $message + * @param string $classname + * @param string $methodName + */ + public function __construct($message, $classname, $methodName) + { + parent::__construct($message); + + $this->classname = $classname; + $this->methodName = $methodName; + } + + public function getClassname() + { + return $this->classname; + } + + public function getMethodName() + { + return $this->methodName; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +class ClassCreatorException extends \RuntimeException implements DoublerException +{ + private $node; + + public function __construct($message, ClassNode $node) + { + parent::__construct($message); + + $this->node = $node; + } + + public function getClassNode() + { + return $this->node; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Doubler; + +class ClassNotFoundException extends DoubleException +{ + private $classname; + + /** + * @param string $message + * @param string $classname + */ + public function __construct($message, $classname) + { + parent::__construct($message); + + $this->classname = $classname; + } + + public function getClassname() + { + return $this->classname; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\Prophecy\MethodProphecyException; + +class UnexpectedCallsException extends MethodProphecyException implements PredictionException +{ + private $calls = array(); + + public function __construct($message, MethodProphecy $methodProphecy, array $calls) + { + parent::__construct($message, $methodProphecy); + + $this->calls = $calls; + } + + public function getCalls() + { + return $this->calls; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Exception\Exception; + +interface PredictionException extends Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use RuntimeException; + +/** + * Basic failed prediction exception. + * Use it for custom prediction failures. + * + * @author Konstantin Kudryashov + */ +class FailedPredictionException extends RuntimeException implements PredictionException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Exception\Prophecy\MethodProphecyException; + +class NoCallsException extends MethodProphecyException implements PredictionException +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\ObjectProphecy; + +class AggregateException extends \RuntimeException implements PredictionException +{ + private $exceptions = array(); + private $objectProphecy; + + public function append(PredictionException $exception) + { + $message = $exception->getMessage(); + $message = ' '.strtr($message, array("\n" => "\n "))."\n"; + + $this->message = rtrim($this->message.$message); + $this->exceptions[] = $exception; + } + + /** + * @return PredictionException[] + */ + public function getExceptions() + { + return $this->exceptions; + } + + public function setObjectProphecy(ObjectProphecy $objectProphecy) + { + $this->objectProphecy = $objectProphecy; + } + + /** + * @return ObjectProphecy + */ + public function getObjectProphecy() + { + return $this->objectProphecy; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Prediction; + +use Prophecy\Prophecy\MethodProphecy; + +class UnexpectedCallsCountException extends UnexpectedCallsException +{ + private $expectedCount; + + public function __construct($message, MethodProphecy $methodProphecy, $count, array $calls) + { + parent::__construct($message, $methodProphecy, $calls); + + $this->expectedCount = intval($count); + } + + public function getExpectedCount() + { + return $this->expectedCount; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception\Call; + +use Prophecy\Exception\Prophecy\ObjectProphecyException; +use Prophecy\Prophecy\ObjectProphecy; + +class UnexpectedCallException extends ObjectProphecyException +{ + private $methodName; + private $arguments; + + public function __construct($message, ObjectProphecy $objectProphecy, + $methodName, array $arguments) + { + parent::__construct($message, $objectProphecy); + + $this->methodName = $methodName; + $this->arguments = $arguments; + } + + public function getMethodName() + { + return $this->methodName; + } + + public function getArguments() + { + return $this->arguments; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Exception; + +class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use ReflectionClass; + +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov + */ +class CachedDoubler extends Doubler +{ + private $classes = array(); + + /** + * {@inheritdoc} + */ + public function registerClassPatch(ClassPatch\ClassPatchInterface $patch) + { + $this->classes[] = array(); + + parent::registerClassPatch($patch); + } + + /** + * {@inheritdoc} + */ + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + { + $classId = $this->generateClassId($class, $interfaces); + if (isset($this->classes[$classId])) { + return $this->classes[$classId]; + } + + return $this->classes[$classId] = parent::createDoubleClass($class, $interfaces); + } + + /** + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + private function generateClassId(ReflectionClass $class = null, array $interfaces) + { + $parts = array(); + if (null !== $class) { + $parts[] = $class->getName(); + } + foreach ($interfaces as $interface) { + $parts[] = $interface->getName(); + } + sort($parts); + + return md5(implode('', $parts)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +/** + * Core double interface. + * All doubled classes will implement this one. + * + * @author Konstantin Kudryashov + */ +interface DoubleInterface +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use Doctrine\Instantiator\Instantiator; +use Prophecy\Doubler\ClassPatch\ClassPatchInterface; +use Prophecy\Doubler\Generator\ClassMirror; +use Prophecy\Doubler\Generator\ClassCreator; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; + +/** + * Cached class doubler. + * Prevents mirroring/creation of the same structure twice. + * + * @author Konstantin Kudryashov + */ +class Doubler +{ + private $mirror; + private $creator; + private $namer; + + /** + * @var ClassPatchInterface[] + */ + private $patches = array(); + + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * Initializes doubler. + * + * @param ClassMirror $mirror + * @param ClassCreator $creator + * @param NameGenerator $namer + */ + public function __construct(ClassMirror $mirror = null, ClassCreator $creator = null, + NameGenerator $namer = null) + { + $this->mirror = $mirror ?: new ClassMirror; + $this->creator = $creator ?: new ClassCreator; + $this->namer = $namer ?: new NameGenerator; + } + + /** + * Returns list of registered class patches. + * + * @return ClassPatchInterface[] + */ + public function getClassPatches() + { + return $this->patches; + } + + /** + * Registers new class patch. + * + * @param ClassPatchInterface $patch + */ + public function registerClassPatch(ClassPatchInterface $patch) + { + $this->patches[] = $patch; + + @usort($this->patches, function (ClassPatchInterface $patch1, ClassPatchInterface $patch2) { + return $patch2->getPriority() - $patch1->getPriority(); + }); + } + + /** + * Creates double from specific class or/and list of interfaces. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces Array of ReflectionClass instances + * @param array $args Constructor arguments + * + * @return DoubleInterface + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function double(ReflectionClass $class = null, array $interfaces, array $args = null) + { + foreach ($interfaces as $interface) { + if (!$interface instanceof ReflectionClass) { + throw new InvalidArgumentException(sprintf( + "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". + "a second argument to `Doubler::double(...)`, but got %s.", + is_object($interface) ? get_class($interface).' class' : gettype($interface) + )); + } + } + + $classname = $this->createDoubleClass($class, $interfaces); + $reflection = new ReflectionClass($classname); + + if (null !== $args) { + return $reflection->newInstanceArgs($args); + } + if ((null === $constructor = $reflection->getConstructor()) + || ($constructor->isPublic() && !$constructor->isFinal())) { + return $reflection->newInstance(); + } + + if (!$this->instantiator) { + $this->instantiator = new Instantiator(); + } + + return $this->instantiator->instantiate($classname); + } + + /** + * Creates double class and returns its FQN. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + protected function createDoubleClass(ReflectionClass $class = null, array $interfaces) + { + $name = $this->namer->name($class, $interfaces); + $node = $this->mirror->reflect($class, $interfaces); + + foreach ($this->patches as $patch) { + if ($patch->supports($node)) { + $patch->apply($node); + } + } + + $this->creator->create($name, $node); + + return $name; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +/** + * Class code creator. + * Generates PHP code for specific class node tree. + * + * @author Konstantin Kudryashov + */ +class ClassCodeGenerator +{ + /** + * Generates PHP code for class node. + * + * @param string $classname + * @param Node\ClassNode $class + * + * @return string + */ + public function generate($classname, Node\ClassNode $class) + { + $parts = explode('\\', $classname); + $classname = array_pop($parts); + $namespace = implode('\\', $parts); + + $code = sprintf("class %s extends \%s implements %s {\n", + $classname, $class->getParentClass(), implode(', ', + array_map(function ($interface) {return '\\'.$interface;}, $class->getInterfaces()) + ) + ); + + foreach ($class->getProperties() as $name => $visibility) { + $code .= sprintf("%s \$%s;\n", $visibility, $name); + } + $code .= "\n"; + + foreach ($class->getMethods() as $method) { + $code .= $this->generateMethod($method)."\n"; + } + $code .= "\n}"; + + return sprintf("namespace %s {\n%s\n}", $namespace, $code); + } + + private function generateMethod(Node\MethodNode $method) + { + $php = sprintf("%s %s function %s%s(%s)%s {\n", + $method->getVisibility(), + $method->isStatic() ? 'static' : '', + $method->returnsReference() ? '&':'', + $method->getName(), + implode(', ', $this->generateArguments($method->getArguments())), + $method->hasReturnType() ? sprintf(': %s', $method->getReturnType()) : '' + ); + $php .= $method->getCode()."\n"; + + return $php.'}'; + } + + private function generateArguments(array $arguments) + { + return array_map(function (Node\ArgumentNode $argument) { + $php = ''; + + if ($hint = $argument->getTypeHint()) { + if ('array' === $hint || 'callable' === $hint) { + $php .= $hint; + } else { + $php .= '\\'.$hint; + } + } + + $php .= ' '.($argument->isPassedByReference() ? '&' : '').'$'.$argument->getName(); + + if ($argument->isOptional()) { + $php .= ' = '.var_export($argument->getDefault(), true); + } + + return $php; + }, $arguments); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +use Prophecy\Exception\Doubler\ClassCreatorException; + +/** + * Class creator. + * Creates specific class in current environment. + * + * @author Konstantin Kudryashov + */ +class ClassCreator +{ + private $generator; + + /** + * Initializes creator. + * + * @param ClassCodeGenerator $generator + */ + public function __construct(ClassCodeGenerator $generator = null) + { + $this->generator = $generator ?: new ClassCodeGenerator; + } + + /** + * Creates class. + * + * @param string $classname + * @param Node\ClassNode $class + * + * @return mixed + * + * @throws \Prophecy\Exception\Doubler\ClassCreatorException + */ + public function create($classname, Node\ClassNode $class) + { + $code = $this->generator->generate($classname, $class); + $return = eval($code); + + if (!class_exists($classname, false)) { + if (count($class->getInterfaces())) { + throw new ClassCreatorException(sprintf( + 'Could not double `%s` and implement interfaces: [%s].', + $class->getParentClass(), implode(', ', $class->getInterfaces()) + ), $class); + } + + throw new ClassCreatorException( + sprintf('Could not double `%s`.', $class->getParentClass()), + $class + ); + } + + return $return; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +/** + * Reflection interface. + * All reflected classes implement this interface. + * + * @author Konstantin Kudryashov + */ +interface ReflectionInterface +{ +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator; + +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Exception\Doubler\ClassMirrorException; +use ReflectionClass; +use ReflectionMethod; +use ReflectionParameter; + +/** + * Class mirror. + * Core doubler class. Mirrors specific class and/or interfaces into class node tree. + * + * @author Konstantin Kudryashov + */ +class ClassMirror +{ + private static $reflectableMethods = array( + '__construct', + '__destruct', + '__sleep', + '__wakeup', + '__toString', + '__call', + ); + + /** + * Reflects provided arguments into class node. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return Node\ClassNode + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function reflect(ReflectionClass $class = null, array $interfaces) + { + $node = new Node\ClassNode; + + if (null !== $class) { + if (true === $class->isInterface()) { + throw new InvalidArgumentException(sprintf( + "Could not reflect %s as a class, because it\n". + "is interface - use the second argument instead.", + $class->getName() + )); + } + + $this->reflectClassToNode($class, $node); + } + + foreach ($interfaces as $interface) { + if (!$interface instanceof ReflectionClass) { + throw new InvalidArgumentException(sprintf( + "[ReflectionClass \$interface1 [, ReflectionClass \$interface2]] array expected as\n". + "a second argument to `ClassMirror::reflect(...)`, but got %s.", + is_object($interface) ? get_class($interface).' class' : gettype($interface) + )); + } + if (false === $interface->isInterface()) { + throw new InvalidArgumentException(sprintf( + "Could not reflect %s as an interface, because it\n". + "is class - use the first argument instead.", + $interface->getName() + )); + } + + $this->reflectInterfaceToNode($interface, $node); + } + + $node->addInterface('Prophecy\Doubler\Generator\ReflectionInterface'); + + return $node; + } + + private function reflectClassToNode(ReflectionClass $class, Node\ClassNode $node) + { + if (true === $class->isFinal()) { + throw new ClassMirrorException(sprintf( + 'Could not reflect class %s as it is marked final.', $class->getName() + ), $class); + } + + $node->setParentClass($class->getName()); + + foreach ($class->getMethods(ReflectionMethod::IS_ABSTRACT) as $method) { + if (false === $method->isProtected()) { + continue; + } + + $this->reflectMethodToNode($method, $node); + } + + foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if (0 === strpos($method->getName(), '_') + && !in_array($method->getName(), self::$reflectableMethods)) { + continue; + } + + if (true === $method->isFinal()) { + continue; + } + + $this->reflectMethodToNode($method, $node); + } + } + + private function reflectInterfaceToNode(ReflectionClass $interface, Node\ClassNode $node) + { + $node->addInterface($interface->getName()); + + foreach ($interface->getMethods() as $method) { + $this->reflectMethodToNode($method, $node); + } + } + + private function reflectMethodToNode(ReflectionMethod $method, Node\ClassNode $classNode) + { + $node = new Node\MethodNode($method->getName()); + + if (true === $method->isProtected()) { + $node->setVisibility('protected'); + } + + if (true === $method->isStatic()) { + $node->setStatic(); + } + + if (true === $method->returnsReference()) { + $node->setReturnsReference(); + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && true === $method->hasReturnType()) { + $node->setReturnType((string) $method->getReturnType()); + } + + if (is_array($params = $method->getParameters()) && count($params)) { + foreach ($params as $param) { + $this->reflectArgumentToNode($param, $node); + } + } + + $classNode->addMethod($node); + } + + private function reflectArgumentToNode(ReflectionParameter $parameter, Node\MethodNode $methodNode) + { + $name = $parameter->getName() == '...' ? '__dot_dot_dot__' : $parameter->getName(); + $node = new Node\ArgumentNode($name); + + $typeHint = $this->getTypeHint($parameter); + $node->setTypeHint($typeHint); + + if (true === $parameter->isDefaultValueAvailable()) { + $node->setDefault($parameter->getDefaultValue()); + } elseif (true === $parameter->isOptional() + || (true === $parameter->allowsNull() && $typeHint)) { + $node->setDefault(null); + } + + if (true === $parameter->isPassedByReference()) { + $node->setAsPassedByReference(); + } + + $methodNode->addArgument($node); + } + + private function getTypeHint(ReflectionParameter $parameter) + { + if (null !== $className = $this->getParameterClassName($parameter)) { + return $className; + } + + if (true === $parameter->isArray()) { + return 'array'; + } + + if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) { + return 'callable'; + } + + if (version_compare(PHP_VERSION, '7.0', '>=') && true === $parameter->hasType()) { + return (string) $parameter->getType(); + } + + return null; + } + + private function getParameterClassName(ReflectionParameter $parameter) + { + try { + return $parameter->getClass() ? $parameter->getClass()->getName() : null; + } catch (\ReflectionException $e) { + preg_match('/\[\s\<\w+?>\s([\w,\\\]+)/s', $parameter, $matches); + + return isset($matches[1]) ? $matches[1] : null; + } + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +/** + * Argument node. + * + * @author Konstantin Kudryashov + */ +class ArgumentNode +{ + private $name; + private $typeHint; + private $default; + private $optional = false; + private $byReference = false; + + /** + * @param string $name + */ + public function __construct($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function getTypeHint() + { + return $this->typeHint; + } + + public function setTypeHint($typeHint = null) + { + $this->typeHint = $typeHint; + } + + public function getDefault() + { + return $this->default; + } + + public function setDefault($default = null) + { + $this->optional = true; + $this->default = $default; + } + + public function isOptional() + { + return $this->optional; + } + + public function setAsPassedByReference($byReference = true) + { + $this->byReference = $byReference; + } + + public function isPassedByReference() + { + return $this->byReference; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Method node. + * + * @author Konstantin Kudryashov + */ +class MethodNode +{ + private $name; + private $code; + private $visibility = 'public'; + private $static = false; + private $returnsReference = false; + private $returnType; + + /** + * @var ArgumentNode[] + */ + private $arguments = array(); + + /** + * @param string $name + * @param string $code + */ + public function __construct($name, $code = null) + { + $this->name = $name; + $this->code = $code; + } + + public function getVisibility() + { + return $this->visibility; + } + + /** + * @param string $visibility + */ + public function setVisibility($visibility) + { + $visibility = strtolower($visibility); + + if (!in_array($visibility, array('public', 'private', 'protected'))) { + throw new InvalidArgumentException(sprintf( + '`%s` method visibility is not supported.', $visibility + )); + } + + $this->visibility = $visibility; + } + + public function isStatic() + { + return $this->static; + } + + public function setStatic($static = true) + { + $this->static = (bool) $static; + } + + public function returnsReference() + { + return $this->returnsReference; + } + + public function setReturnsReference() + { + $this->returnsReference = true; + } + + public function getName() + { + return $this->name; + } + + public function addArgument(ArgumentNode $argument) + { + $this->arguments[] = $argument; + } + + /** + * @return ArgumentNode[] + */ + public function getArguments() + { + return $this->arguments; + } + + public function hasReturnType() + { + return null !== $this->returnType; + } + + /** + * @param string $type + */ + public function setReturnType($type = null) + { + switch ($type) { + case '': + $this->returnType = null; + break; + + case 'string'; + case 'float': + case 'int': + case 'bool': + case 'array': + case 'callable': + $this->returnType = $type; + break; + + case 'double': + case 'real': + $this->returnType = 'float'; + break; + + case 'boolean': + $this->returnType = 'bool'; + break; + + case 'integer': + $this->returnType = 'int'; + break; + + default: + $this->returnType = '\\' . ltrim($type, '\\'); + } + } + + public function getReturnType() + { + return $this->returnType; + } + + /** + * @param string $code + */ + public function setCode($code) + { + $this->code = $code; + } + + public function getCode() + { + if ($this->returnsReference) + { + return "throw new \Prophecy\Exception\Doubler\ReturnByReferenceException('Returning by reference not supported', get_class(\$this), '{$this->name}');"; + } + + return (string) $this->code; + } + + public function useParentCode() + { + $this->code = sprintf( + 'return parent::%s(%s);', $this->getName(), implode(', ', + array_map(function (ArgumentNode $arg) { return '$'.$arg->getName(); }, $this->arguments) + ) + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\Generator\Node; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Class node. + * + * @author Konstantin Kudryashov + */ +class ClassNode +{ + private $parentClass = 'stdClass'; + private $interfaces = array(); + private $properties = array(); + + /** + * @var MethodNode[] + */ + private $methods = array(); + + public function getParentClass() + { + return $this->parentClass; + } + + /** + * @param string $class + */ + public function setParentClass($class) + { + $this->parentClass = $class ?: 'stdClass'; + } + + /** + * @return string[] + */ + public function getInterfaces() + { + return $this->interfaces; + } + + /** + * @param string $interface + */ + public function addInterface($interface) + { + if ($this->hasInterface($interface)) { + return; + } + + array_unshift($this->interfaces, $interface); + } + + /** + * @param string $interface + * + * @return bool + */ + public function hasInterface($interface) + { + return in_array($interface, $this->interfaces); + } + + public function getProperties() + { + return $this->properties; + } + + public function addProperty($name, $visibility = 'public') + { + $visibility = strtolower($visibility); + + if (!in_array($visibility, array('public', 'private', 'protected'))) { + throw new InvalidArgumentException(sprintf( + '`%s` property visibility is not supported.', $visibility + )); + } + + $this->properties[$name] = $visibility; + } + + /** + * @return MethodNode[] + */ + public function getMethods() + { + return $this->methods; + } + + public function addMethod(MethodNode $method) + { + $this->methods[$method->getName()] = $method; + } + + public function removeMethod($name) + { + unset($this->methods[$name]); + } + + /** + * @param string $name + * + * @return MethodNode|null + */ + public function getMethod($name) + { + return $this->hasMethod($name) ? $this->methods[$name] : null; + } + + /** + * @param string $name + * + * @return bool + */ + public function hasMethod($name) + { + return isset($this->methods[$name]); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use Prophecy\Exception\Doubler\DoubleException; +use Prophecy\Exception\Doubler\ClassNotFoundException; +use Prophecy\Exception\Doubler\InterfaceNotFoundException; +use ReflectionClass; + +/** + * Lazy double. + * Gives simple interface to describe double before creating it. + * + * @author Konstantin Kudryashov + */ +class LazyDouble +{ + private $doubler; + private $class; + private $interfaces = array(); + private $arguments = null; + private $double; + + /** + * Initializes lazy double. + * + * @param Doubler $doubler + */ + public function __construct(Doubler $doubler) + { + $this->doubler = $doubler; + } + + /** + * Tells doubler to use specific class as parent one for double. + * + * @param string|ReflectionClass $class + * + * @throws \Prophecy\Exception\Doubler\ClassNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException + */ + public function setParentClass($class) + { + if (null !== $this->double) { + throw new DoubleException('Can not extend class with already instantiated double.'); + } + + if (!$class instanceof ReflectionClass) { + if (!class_exists($class)) { + throw new ClassNotFoundException(sprintf('Class %s not found.', $class), $class); + } + + $class = new ReflectionClass($class); + } + + $this->class = $class; + } + + /** + * Tells doubler to implement specific interface with double. + * + * @param string|ReflectionClass $interface + * + * @throws \Prophecy\Exception\Doubler\InterfaceNotFoundException + * @throws \Prophecy\Exception\Doubler\DoubleException + */ + public function addInterface($interface) + { + if (null !== $this->double) { + throw new DoubleException( + 'Can not implement interface with already instantiated double.' + ); + } + + if (!$interface instanceof ReflectionClass) { + if (!interface_exists($interface)) { + throw new InterfaceNotFoundException( + sprintf('Interface %s not found.', $interface), + $interface + ); + } + + $interface = new ReflectionClass($interface); + } + + $this->interfaces[] = $interface; + } + + /** + * Sets constructor arguments. + * + * @param array $arguments + */ + public function setArguments(array $arguments = null) + { + $this->arguments = $arguments; + } + + /** + * Creates double instance or returns already created one. + * + * @return DoubleInterface + */ + public function getInstance() + { + if (null === $this->double) { + if (null !== $this->arguments) { + return $this->double = $this->doubler->double( + $this->class, $this->interfaces, $this->arguments + ); + } + + $this->double = $this->doubler->double($this->class, $this->interfaces); + } + + return $this->double; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Remove method functionality from the double which will clash with php keywords. + * + * @author Milan Magudia + */ +class KeywordPatch implements ClassPatchInterface +{ + /** + * Support any class + * + * @param ClassNode $node + * + * @return boolean + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Remove methods that clash with php keywords + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $methodNames = array_keys($node->getMethods()); + $methodsToRemove = array_intersect($methodNames, $this->getKeywords()); + foreach ($methodsToRemove as $methodName) { + $node->removeMethod($methodName); + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() { + return 49; + } + + /** + * Returns array of php keywords. + * + * @return array + */ + private function getKeywords() { + + return array( + '__halt_compiler', + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'callable', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'echo', + 'else', + 'elseif', + 'empty', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'eval', + 'exit', + 'extends', + 'final', + 'finally', + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'include', + 'include_once', + 'instanceof', + 'insteadof', + 'interface', + 'isset', + 'list', + 'namespace', + 'new', + 'or', + 'print', + 'private', + 'protected', + 'public', + 'require', + 'require_once', + 'return', + 'static', + 'switch', + 'throw', + 'trait', + 'try', + 'unset', + 'use', + 'var', + 'while', + 'xor', + 'yield', + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; +use Prophecy\Doubler\Generator\Node\ArgumentNode; + +/** + * Add Prophecy functionality to the double. + * This is a core class patch for Prophecy. + * + * @author Konstantin Kudryashov + */ +class ProphecySubjectPatch implements ClassPatchInterface +{ + /** + * Always returns true. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Apply Prophecy functionality to class node. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $node->addInterface('Prophecy\Prophecy\ProphecySubjectInterface'); + $node->addProperty('objectProphecy', 'private'); + + foreach ($node->getMethods() as $name => $method) { + if ('__construct' === strtolower($name)) { + continue; + } + + $method->setCode( + 'return $this->getProphecy()->makeProphecyMethodCall(__FUNCTION__, func_get_args());' + ); + } + + $prophecySetter = new MethodNode('setProphecy'); + $prophecyArgument = new ArgumentNode('prophecy'); + $prophecyArgument->setTypeHint('Prophecy\Prophecy\ProphecyInterface'); + $prophecySetter->addArgument($prophecyArgument); + $prophecySetter->setCode('$this->objectProphecy = $prophecy;'); + + $prophecyGetter = new MethodNode('getProphecy'); + $prophecyGetter->setCode('return $this->objectProphecy;'); + + if ($node->hasMethod('__call')) { + $__call = $node->getMethod('__call'); + } else { + $__call = new MethodNode('__call'); + $__call->addArgument(new ArgumentNode('name')); + $__call->addArgument(new ArgumentNode('arguments')); + + $node->addMethod($__call); + } + + $__call->setCode(<<getProphecy(), func_get_arg(0) +); +PHP + ); + + $node->addMethod($prophecySetter); + $node->addMethod($prophecyGetter); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 0; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use phpDocumentor\Reflection\DocBlock; +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * Discover Magical API using "@method" PHPDoc format. + * + * @author Thomas Tourlourat + */ +class MagicCallPatch implements ClassPatchInterface +{ + /** + * Support any class + * + * @param ClassNode $node + * + * @return boolean + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Discover Magical API + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $parentClass = $node->getParentClass(); + $reflectionClass = new \ReflectionClass($parentClass); + + $phpdoc = new DocBlock($reflectionClass->getDocComment()); + + $tagList = $phpdoc->getTagsByName('method'); + + foreach($tagList as $tag) { + $methodName = $tag->getMethodName(); + + if (!$reflectionClass->hasMethod($methodName)) { + $methodNode = new MethodNode($tag->getMethodName()); + $methodNode->setStatic($tag->isStatic()); + + $node->addMethod($methodNode); + } + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return integer Priority number (higher - earlier) + */ + public function getPriority() + { + return 50; + } +} + + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * Traversable interface patch. + * Forces classes that implement interfaces, that extend Traversable to also implement Iterator. + * + * @author Konstantin Kudryashov + */ +class TraversablePatch implements ClassPatchInterface +{ + /** + * Supports nodetree, that implement Traversable, but not Iterator or IteratorAggregate. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (in_array('Iterator', $node->getInterfaces())) { + return false; + } + if (in_array('IteratorAggregate', $node->getInterfaces())) { + return false; + } + + foreach ($node->getInterfaces() as $interface) { + if ('Traversable' !== $interface && !is_subclass_of($interface, 'Traversable')) { + continue; + } + if ('Iterator' === $interface || is_subclass_of($interface, 'Iterator')) { + continue; + } + if ('IteratorAggregate' === $interface || is_subclass_of($interface, 'IteratorAggregate')) { + continue; + } + + return true; + } + + return false; + } + + /** + * Forces class to implement Iterator interface. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + $node->addInterface('Iterator'); + + $node->addMethod(new MethodNode('current')); + $node->addMethod(new MethodNode('key')); + $node->addMethod(new MethodNode('next')); + $node->addMethod(new MethodNode('rewind')); + $node->addMethod(new MethodNode('valid')); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 100; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * SplFileInfo patch. + * Makes SplFileInfo and derivative classes usable with Prophecy. + * + * @author Konstantin Kudryashov + */ +class SplFileInfoPatch implements ClassPatchInterface +{ + /** + * Supports everything that extends SplFileInfo. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (null === $node->getParentClass()) { + return false; + } + + return 'SplFileInfo' === $node->getParentClass() + || is_subclass_of($node->getParentClass(), 'SplFileInfo') + ; + } + + /** + * Updated constructor code to call parent one with dummy file argument. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + if ($node->hasMethod('__construct')) { + $constructor = $node->getMethod('__construct'); + } else { + $constructor = new MethodNode('__construct'); + $node->addMethod($constructor); + } + + if ($this->nodeIsDirectoryIterator($node)) { + $constructor->setCode('return parent::__construct("' . __DIR__ . '");'); + return; + } + + $constructor->useParentCode(); + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority() + { + return 50; + } + + /** + * @param ClassNode $node + * @return boolean + */ + private function nodeIsDirectoryIterator(ClassNode $node) + { + $parent = $node->getParentClass(); + return 'DirectoryIterator' === $parent + || is_subclass_of($parent, 'DirectoryIterator'); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; +use Prophecy\Doubler\Generator\Node\MethodNode; + +/** + * Disable constructor. + * Makes all constructor arguments optional. + * + * @author Konstantin Kudryashov + */ +class DisableConstructorPatch implements ClassPatchInterface +{ + /** + * Checks if class has `__construct` method. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return true; + } + + /** + * Makes all class constructor arguments optional. + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + if (!$node->hasMethod('__construct')) { + $node->addMethod(new MethodNode('__construct', '')); + + return; + } + + $constructor = $node->getMethod('__construct'); + foreach ($constructor->getArguments() as $argument) { + $argument->setDefault(null); + } + + $constructor->setCode(<< + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Exception patch for HHVM to remove the stubs from special methods + * + * @author Christophe Coevoet + */ +class HhvmExceptionPatch implements ClassPatchInterface +{ + /** + * Supports exceptions on HHVM. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + if (!defined('HHVM_VERSION')) { + return false; + } + + return 'Exception' === $node->getParentClass() || is_subclass_of($node->getParentClass(), 'Exception'); + } + + /** + * Removes special exception static methods from the doubled methods. + * + * @param ClassNode $node + * + * @return void + */ + public function apply(ClassNode $node) + { + if ($node->hasMethod('setTraceOptions')) { + $node->getMethod('setTraceOptions')->useParentCode(); + } + if ($node->hasMethod('getTraceOptions')) { + $node->getMethod('getTraceOptions')->useParentCode(); + } + } + + /** + * {@inheritdoc} + */ + public function getPriority() + { + return -50; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * ReflectionClass::newInstance patch. + * Makes first argument of newInstance optional, since it works but signature is misleading + * + * @author Florian Klein + */ +class ReflectionClassNewInstancePatch implements ClassPatchInterface +{ + /** + * Supports ReflectionClass + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node) + { + return 'ReflectionClass' === $node->getParentClass(); + } + + /** + * Updates newInstance's first argument to make it optional + * + * @param ClassNode $node + */ + public function apply(ClassNode $node) + { + foreach ($node->getMethod('newInstance')->getArguments() as $argument) { + $argument->setDefault(null); + } + } + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher = earlier) + */ + public function getPriority() + { + return 50; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler\ClassPatch; + +use Prophecy\Doubler\Generator\Node\ClassNode; + +/** + * Class patch interface. + * Class patches extend doubles functionality or help + * Prophecy to avoid some internal PHP bugs. + * + * @author Konstantin Kudryashov + */ +interface ClassPatchInterface +{ + /** + * Checks if patch supports specific class node. + * + * @param ClassNode $node + * + * @return bool + */ + public function supports(ClassNode $node); + + /** + * Applies patch to the specific class node. + * + * @param ClassNode $node + * @return void + */ + public function apply(ClassNode $node); + + /** + * Returns patch priority, which determines when patch will be applied. + * + * @return int Priority number (higher - earlier) + */ + public function getPriority(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Doubler; + +use ReflectionClass; + +/** + * Name generator. + * Generates classname for double. + * + * @author Konstantin Kudryashov + */ +class NameGenerator +{ + private static $counter = 1; + + /** + * Generates name. + * + * @param ReflectionClass $class + * @param ReflectionClass[] $interfaces + * + * @return string + */ + public function name(ReflectionClass $class = null, array $interfaces) + { + $parts = array(); + + if (null !== $class) { + $parts[] = $class->getName(); + } else { + foreach ($interfaces as $interface) { + $parts[] = $interface->getShortName(); + } + } + + if (!count($parts)) { + $parts[] = 'stdClass'; + } + + return sprintf('Double\%s\P%d', implode('\\', $parts), self::$counter++); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Argument\Token\AnyValuesToken; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\UnexpectedCallsCountException; + +/** + * Prediction interface. + * Predictions are logical test blocks, tied to `should...` keyword. + * + * @author Konstantin Kudryashov + */ +class CallTimesPrediction implements PredictionInterface +{ + private $times; + private $util; + + /** + * Initializes prediction. + * + * @param int $times + * @param StringUtil $util + */ + public function __construct($times, StringUtil $util = null) + { + $this->times = intval($times); + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there was exact amount of calls made. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\UnexpectedCallsCountException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if ($this->times == count($calls)) { + return; + } + + $methodCalls = $object->findProphecyMethodCalls( + $method->getMethodName(), + new ArgumentsWildcard(array(new AnyValuesToken)) + ); + + if (count($calls)) { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but %d were made:\n%s", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + count($calls), + $this->util->stringifyCalls($calls) + ); + } elseif (count($methodCalls)) { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but none were made.\n". + "Recorded `%s(...)` calls:\n%s", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + $method->getMethodName(), + $this->util->stringifyCalls($methodCalls) + ); + } else { + $message = sprintf( + "Expected exactly %d calls that match:\n". + " %s->%s(%s)\n". + "but none were made.", + + $this->times, + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard() + ); + } + + throw new UnexpectedCallsCountException($message, $method, $this->times, $calls); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\UnexpectedCallsException; + +/** + * No calls prediction. + * + * @author Konstantin Kudryashov + */ +class NoCallsPrediction implements PredictionInterface +{ + private $util; + + /** + * Initializes prediction. + * + * @param null|StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there were no calls made. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\UnexpectedCallsException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if (!count($calls)) { + return; + } + + $verb = count($calls) === 1 ? 'was' : 'were'; + + throw new UnexpectedCallsException(sprintf( + "No calls expected that match:\n". + " %s->%s(%s)\n". + "but %d %s made:\n%s", + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + count($calls), + $verb, + $this->util->stringifyCalls($calls) + ), $method, $calls); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Prediction interface. + * Predictions are logical test blocks, tied to `should...` keyword. + * + * @author Konstantin Kudryashov + */ +interface PredictionInterface +{ + /** + * Tests that double fulfilled prediction. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws object + * @return void + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; + +/** + * Callback prediction. + * + * @author Konstantin Kudryashov + */ +class CallbackPrediction implements PredictionInterface +{ + private $callback; + + /** + * Initializes callback prediction. + * + * @param callable $callback Custom callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackPrediction, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Executes preset callback. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + $callback = $this->callback; + + if ($callback instanceof Closure && method_exists('Closure', 'bind')) { + $callback = Closure::bind($callback, $object); + } + + call_user_func($callback, $calls, $object, $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Prediction; + +use Prophecy\Call\Call; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Argument\Token\AnyValuesToken; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Prediction\NoCallsException; + +/** + * Call prediction. + * + * @author Konstantin Kudryashov + */ +class CallPrediction implements PredictionInterface +{ + private $util; + + /** + * Initializes prediction. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Tests that there was at least one call. + * + * @param Call[] $calls + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws \Prophecy\Exception\Prediction\NoCallsException + */ + public function check(array $calls, ObjectProphecy $object, MethodProphecy $method) + { + if (count($calls)) { + return; + } + + $methodCalls = $object->findProphecyMethodCalls( + $method->getMethodName(), + new ArgumentsWildcard(array(new AnyValuesToken)) + ); + + if (count($methodCalls)) { + throw new NoCallsException(sprintf( + "No calls have been made that match:\n". + " %s->%s(%s)\n". + "but expected at least one.\n". + "Recorded `%s(...)` calls:\n%s", + + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard(), + $method->getMethodName(), + $this->util->stringifyCalls($methodCalls) + ), $method); + } + + throw new NoCallsException(sprintf( + "No calls have been made that match:\n". + " %s->%s(%s)\n". + "but expected at least one.", + + get_class($object->reveal()), + $method->getMethodName(), + $method->getArgumentsWildcard() + ), $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Call; + +use Exception; + +/** + * Call object. + * + * @author Konstantin Kudryashov + */ +class Call +{ + private $methodName; + private $arguments; + private $returnValue; + private $exception; + private $file; + private $line; + + /** + * Initializes call. + * + * @param string $methodName + * @param array $arguments + * @param mixed $returnValue + * @param Exception $exception + * @param null|string $file + * @param null|int $line + */ + public function __construct($methodName, array $arguments, $returnValue, + Exception $exception = null, $file, $line) + { + $this->methodName = $methodName; + $this->arguments = $arguments; + $this->returnValue = $returnValue; + $this->exception = $exception; + + if ($file) { + $this->file = $file; + $this->line = intval($line); + } + } + + /** + * Returns called method name. + * + * @return string + */ + public function getMethodName() + { + return $this->methodName; + } + + /** + * Returns called method arguments. + * + * @return array + */ + public function getArguments() + { + return $this->arguments; + } + + /** + * Returns called method return value. + * + * @return null|mixed + */ + public function getReturnValue() + { + return $this->returnValue; + } + + /** + * Returns exception that call thrown. + * + * @return null|Exception + */ + public function getException() + { + return $this->exception; + } + + /** + * Returns callee filename. + * + * @return string + */ + public function getFile() + { + return $this->file; + } + + /** + * Returns callee line number. + * + * @return int + */ + public function getLine() + { + return $this->line; + } + + /** + * Returns short notation for callee place. + * + * @return string + */ + public function getCallPlace() + { + if (null === $this->file) { + return 'unknown'; + } + + return sprintf('%s:%d', $this->file, $this->line); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Call; + +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Argument\ArgumentsWildcard; +use Prophecy\Util\StringUtil; +use Prophecy\Exception\Call\UnexpectedCallException; + +/** + * Calls receiver & manager. + * + * @author Konstantin Kudryashov + */ +class CallCenter +{ + private $util; + + /** + * @var Call[] + */ + private $recordedCalls = array(); + + /** + * Initializes call center. + * + * @param StringUtil $util + */ + public function __construct(StringUtil $util = null) + { + $this->util = $util ?: new StringUtil; + } + + /** + * Makes and records specific method call for object prophecy. + * + * @param ObjectProphecy $prophecy + * @param string $methodName + * @param array $arguments + * + * @return mixed Returns null if no promise for prophecy found or promise return value. + * + * @throws \Prophecy\Exception\Call\UnexpectedCallException If no appropriate method prophecy found + */ + public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments) + { + $backtrace = debug_backtrace(); + + $file = $line = null; + if (isset($backtrace[2]) && isset($backtrace[2]['file'])) { + $file = $backtrace[2]['file']; + $line = $backtrace[2]['line']; + } + + // If no method prophecies defined, then it's a dummy, so we'll just return null + if ('__destruct' === $methodName || 0 == count($prophecy->getMethodProphecies())) { + $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); + + return null; + } + + // There are method prophecies, so it's a fake/stub. Searching prophecy for this call + $matches = array(); + foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { + if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { + $matches[] = array($score, $methodProphecy); + } + } + + // If fake/stub doesn't have method prophecy for this call - throw exception + if (!count($matches)) { + throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments); + } + + // Sort matches by their score value + @usort($matches, function ($match1, $match2) { return $match2[0] - $match1[0]; }); + + // If Highest rated method prophecy has a promise - execute it or return null instead + $returnValue = null; + $exception = null; + if ($promise = $matches[0][1]->getPromise()) { + try { + $returnValue = $promise->execute($arguments, $prophecy, $matches[0][1]); + } catch (\Exception $e) { + $exception = $e; + } + } + + $this->recordedCalls[] = new Call( + $methodName, $arguments, $returnValue, $exception, $file, $line + ); + + if (null !== $exception) { + throw $exception; + } + + return $returnValue; + } + + /** + * Searches for calls by method name & arguments wildcard. + * + * @param string $methodName + * @param ArgumentsWildcard $wildcard + * + * @return Call[] + */ + public function findCalls($methodName, ArgumentsWildcard $wildcard) + { + return array_values( + array_filter($this->recordedCalls, function (Call $call) use ($methodName, $wildcard) { + return $methodName === $call->getMethodName() + && 0 < $wildcard->scoreArguments($call->getArguments()) + ; + }) + ); + } + + private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, + array $arguments) + { + $classname = get_class($prophecy->reveal()); + $argstring = implode(', ', array_map(array($this->util, 'stringify'), $arguments)); + $expected = implode("\n", array_map(function (MethodProphecy $methodProphecy) { + return sprintf(' - %s(%s)', + $methodProphecy->getMethodName(), + $methodProphecy->getArgumentsWildcard() + ); + }, call_user_func_array('array_merge', $prophecy->getMethodProphecies()))); + + return new UnexpectedCallException( + sprintf( + "Method call:\n". + " - %s(%s)\n". + "on %s was not expected, expected calls were:\n%s", + + $methodName, $argstring, $classname, $expected + ), + $prophecy, $methodName, $arguments + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Doctrine\Instantiator\Instantiator; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use ReflectionClass; + +/** + * Throw promise. + * + * @author Konstantin Kudryashov + */ +class ThrowPromise implements PromiseInterface +{ + private $exception; + + /** + * @var \Doctrine\Instantiator\Instantiator + */ + private $instantiator; + + /** + * Initializes promise. + * + * @param string|\Exception $exception Exception class name or instance + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($exception) + { + if (is_string($exception)) { + if (!class_exists($exception) + && 'Exception' !== $exception + && !is_subclass_of($exception, 'Exception')) { + throw new InvalidArgumentException(sprintf( + 'Exception class or instance expected as argument to ThrowPromise, but got %s.', + gettype($exception) + )); + } + } elseif (!$exception instanceof \Exception) { + throw new InvalidArgumentException(sprintf( + 'Exception class or instance expected as argument to ThrowPromise, but got %s.', + gettype($exception) + )); + } + + $this->exception = $exception; + } + + /** + * Throws predefined exception. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @throws object + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + if (is_string($this->exception)) { + $classname = $this->exception; + $reflection = new ReflectionClass($classname); + $constructor = $reflection->getConstructor(); + + if ($constructor->isPublic() && 0 == $constructor->getNumberOfRequiredParameters()) { + throw $reflection->newInstance(); + } + + if (!$this->instantiator) { + $this->instantiator = new Instantiator(); + } + + throw $this->instantiator->instantiate($classname); + } + + throw $this->exception; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Exception\InvalidArgumentException; +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Return argument promise. + * + * @author Konstantin Kudryashov + */ +class ReturnArgumentPromise implements PromiseInterface +{ + /** + * @var int + */ + private $index; + + /** + * Initializes callback promise. + * + * @param int $index The zero-indexed number of the argument to return + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($index = 0) + { + if (!is_int($index) || $index < 0) { + throw new InvalidArgumentException( + 'Zero-based index expected as argument to ReturnArgumentPromise, but got %s.', + $index + ); + } + $this->index = $index; + } + + /** + * Returns nth argument if has one, null otherwise. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return null|mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + return count($args) > $this->index ? $args[$this->index] : null; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Return promise. + * + * @author Konstantin Kudryashov + */ +class ReturnPromise implements PromiseInterface +{ + private $returnValues = array(); + + /** + * Initializes promise. + * + * @param array $returnValues Array of values + */ + public function __construct(array $returnValues) + { + $this->returnValues = $returnValues; + } + + /** + * Returns saved values one by one until last one, then continuously returns last value. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + $value = array_shift($this->returnValues); + + if (!count($this->returnValues)) { + $this->returnValues[] = $value; + } + + return $value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; +use Prophecy\Exception\InvalidArgumentException; +use Closure; + +/** + * Callback promise. + * + * @author Konstantin Kudryashov + */ +class CallbackPromise implements PromiseInterface +{ + private $callback; + + /** + * Initializes callback promise. + * + * @param callable $callback Custom callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackPromise, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Evaluates promise callback. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method) + { + $callback = $this->callback; + + if ($callback instanceof Closure && method_exists('Closure', 'bind')) { + $callback = Closure::bind($callback, $object); + } + + return call_user_func($callback, $args, $object, $method); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Promise; + +use Prophecy\Prophecy\ObjectProphecy; +use Prophecy\Prophecy\MethodProphecy; + +/** + * Promise interface. + * Promises are logical blocks, tied to `will...` keyword. + * + * @author Konstantin Kudryashov + */ +interface PromiseInterface +{ + /** + * Evaluates promise. + * + * @param array $args + * @param ObjectProphecy $object + * @param MethodProphecy $method + * + * @return mixed + */ + public function execute(array $args, ObjectProphecy $object, MethodProphecy $method); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument; + +/** + * Arguments wildcarding. + * + * @author Konstantin Kudryashov + */ +class ArgumentsWildcard +{ + /** + * @var Token\TokenInterface[] + */ + private $tokens = array(); + private $string; + + /** + * Initializes wildcard. + * + * @param array $arguments Array of argument tokens or values + */ + public function __construct(array $arguments) + { + foreach ($arguments as $argument) { + if (!$argument instanceof Token\TokenInterface) { + $argument = new Token\ExactValueToken($argument); + } + + $this->tokens[] = $argument; + } + } + + /** + * Calculates wildcard match score for provided arguments. + * + * @param array $arguments + * + * @return false|int False OR integer score (higher - better) + */ + public function scoreArguments(array $arguments) + { + if (0 == count($arguments) && 0 == count($this->tokens)) { + return 1; + } + + $arguments = array_values($arguments); + $totalScore = 0; + foreach ($this->tokens as $i => $token) { + $argument = isset($arguments[$i]) ? $arguments[$i] : null; + if (1 >= $score = $token->scoreArgument($argument)) { + return false; + } + + $totalScore += $score; + + if (true === $token->isLast()) { + return $totalScore; + } + } + + if (count($arguments) > count($this->tokens)) { + return false; + } + + return $totalScore; + } + + /** + * Returns string representation for wildcard. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = implode(', ', array_map(function ($token) { + return (string) $token; + }, $this->tokens)); + } + + return $this->string; + } + + /** + * @return array + */ + public function getTokens() + { + return $this->tokens; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Any single value token. + * + * @author Konstantin Kudryashov + */ +class AnyValueToken implements TokenInterface +{ + /** + * Always scores 3 for any argument. + * + * @param $argument + * + * @return int + */ + public function scoreArgument($argument) + { + return 3; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return '*'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Array every entry token. + * + * @author Adrien Brault + */ +class ArrayEveryEntryToken implements TokenInterface +{ + /** + * @var TokenInterface + */ + private $value; + + /** + * @param mixed $value exact value or token + */ + public function __construct($value) + { + if (!$value instanceof TokenInterface) { + $value = new ExactValueToken($value); + } + + $this->value = $value; + } + + /** + * {@inheritdoc} + */ + public function scoreArgument($argument) + { + if (!$argument instanceof \Traversable && !is_array($argument)) { + return false; + } + + $scores = array(); + foreach ($argument as $key => $argumentEntry) { + $scores[] = $this->value->scoreArgument($argumentEntry); + } + + if (empty($scores) || in_array(false, $scores, true)) { + return false; + } + + return array_sum($scores) / count($scores); + } + + /** + * {@inheritdoc} + */ + public function isLast() + { + return false; + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return sprintf('[%s, ..., %s]', $this->value, $this->value); + } + + /** + * @return TokenInterface + */ + public function getValue() + { + return $this->value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Logical AND token. + * + * @author Boris Mikhaylov + */ +class LogicalAndToken implements TokenInterface +{ + private $tokens = array(); + + /** + * @param array $arguments exact values or tokens + */ + public function __construct(array $arguments) + { + foreach ($arguments as $argument) { + if (!$argument instanceof TokenInterface) { + $argument = new ExactValueToken($argument); + } + $this->tokens[] = $argument; + } + } + + /** + * Scores maximum score from scores returned by tokens for this argument if all of them score. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (0 === count($this->tokens)) { + return false; + } + + $maxScore = 0; + foreach ($this->tokens as $token) { + $score = $token->scoreArgument($argument); + if (false === $score) { + return false; + } + $maxScore = max($score, $maxScore); + } + + return $maxScore; + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('bool(%s)', implode(' AND ', $this->tokens)); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Argument token interface. + * + * @author Konstantin Kudryashov + */ +interface TokenInterface +{ + /** + * Calculates token match score for provided argument. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument); + + /** + * Returns true if this token prevents check of other tokens (is last one). + * + * @return bool|int + */ + public function isLast(); + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString(); +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Array entry token. + * + * @author Boris Mikhaylov + */ +class ArrayEntryToken implements TokenInterface +{ + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $key; + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $value; + + /** + * @param mixed $key exact value or token + * @param mixed $value exact value or token + */ + public function __construct($key, $value) + { + $this->key = $this->wrapIntoExactValueToken($key); + $this->value = $this->wrapIntoExactValueToken($value); + } + + /** + * Scores half of combined scores from key and value tokens for same entry. Capped at 8. + * If argument implements \ArrayAccess without \Traversable, then key token is restricted to ExactValueToken. + * + * @param array|\ArrayAccess|\Traversable $argument + * + * @throws \Prophecy\Exception\InvalidArgumentException + * @return bool|int + */ + public function scoreArgument($argument) + { + if ($argument instanceof \Traversable) { + $argument = iterator_to_array($argument); + } + + if ($argument instanceof \ArrayAccess) { + $argument = $this->convertArrayAccessToEntry($argument); + } + + if (!is_array($argument) || empty($argument)) { + return false; + } + + $keyScores = array_map(array($this->key,'scoreArgument'), array_keys($argument)); + $valueScores = array_map(array($this->value,'scoreArgument'), $argument); + $scoreEntry = function ($value, $key) { + return $value && $key ? min(8, ($key + $value) / 2) : false; + }; + + return max(array_map($scoreEntry, $valueScores, $keyScores)); + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('[..., %s => %s, ...]', $this->key, $this->value); + } + + /** + * Returns key + * + * @return TokenInterface + */ + public function getKey() + { + return $this->key; + } + + /** + * Returns value + * + * @return TokenInterface + */ + public function getValue() + { + return $this->value; + } + + /** + * Wraps non token $value into ExactValueToken + * + * @param $value + * @return TokenInterface + */ + private function wrapIntoExactValueToken($value) + { + return $value instanceof TokenInterface ? $value : new ExactValueToken($value); + } + + /** + * Converts instance of \ArrayAccess to key => value array entry + * + * @param \ArrayAccess $object + * + * @return array|null + * @throws \Prophecy\Exception\InvalidArgumentException + */ + private function convertArrayAccessToEntry(\ArrayAccess $object) + { + if (!$this->key instanceof ExactValueToken) { + throw new InvalidArgumentException(sprintf( + 'You can only use exact value tokens to match key of ArrayAccess object'.PHP_EOL. + 'But you used `%s`.', + $this->key + )); + } + + $key = $this->key->getValue(); + + return $object->offsetExists($key) ? array($key => $object[$key]) : array(); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Array elements count token. + * + * @author Boris Mikhaylov + */ + +class ArrayCountToken implements TokenInterface +{ + private $count; + + /** + * @param integer $value + */ + public function __construct($value) + { + $this->count = $value; + } + + /** + * Scores 6 when argument has preset number of elements. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return $this->isCountable($argument) && $this->hasProperCount($argument) ? 6 : false; + } + + /** + * Returns false. + * + * @return boolean + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('count(%s)', $this->count); + } + + /** + * Returns true if object is either array or instance of \Countable + * + * @param $argument + * @return bool + */ + private function isCountable($argument) + { + return (is_array($argument) || $argument instanceof \Countable); + } + + /** + * Returns true if $argument has expected number of elements + * + * @param array|\Countable $argument + * + * @return bool + */ + private function hasProperCount($argument) + { + return $this->count === count($argument); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Any values token. + * + * @author Konstantin Kudryashov + */ +class AnyValuesToken implements TokenInterface +{ + /** + * Always scores 2 for any argument. + * + * @param $argument + * + * @return int + */ + public function scoreArgument($argument) + { + return 2; + } + + /** + * Returns true to stop wildcard from processing other tokens. + * + * @return bool + */ + public function isLast() + { + return true; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return '* [, ...]'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * String contains token. + * + * @author Peter Mitchell + */ +class StringContainsToken implements TokenInterface +{ + private $value; + + /** + * Initializes token. + * + * @param string $value + */ + public function __construct($value) + { + $this->value = $value; + } + + public function scoreArgument($argument) + { + return strpos($argument, $this->value) !== false ? 6 : false; + } + + /** + * Returns preset value against which token checks arguments. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('contains("%s")', $this->value); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; + +/** + * Exact value token. + * + * @author Konstantin Kudryashov + */ +class ExactValueToken implements TokenInterface +{ + private $value; + private $string; + private $util; + private $comparatorFactory; + + /** + * Initializes token. + * + * @param mixed $value + * @param StringUtil $util + * @param ComparatorFactory $comparatorFactory + */ + public function __construct($value, StringUtil $util = null, ComparatorFactory $comparatorFactory = null) + { + $this->value = $value; + $this->util = $util ?: new StringUtil(); + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Scores 10 if argument matches preset value. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (is_object($argument) && is_object($this->value)) { + $comparator = $this->comparatorFactory->getComparatorFor( + $argument, $this->value + ); + + try { + $comparator->assertEquals($argument, $this->value); + return 10; + } catch (ComparisonFailure $failure) {} + } + + // If either one is an object it should be castable to a string + if (is_object($argument) xor is_object($this->value)) { + if (is_object($argument) && !method_exists($argument, '__toString')) { + return false; + } + + if (is_object($this->value) && !method_exists($this->value, '__toString')) { + return false; + } + } elseif (is_numeric($argument) && is_numeric($this->value)) { + // noop + } elseif (gettype($argument) !== gettype($this->value)) { + return false; + } + + return $argument == $this->value ? 10 : false; + } + + /** + * Returns preset value against which token checks arguments. + * + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = sprintf('exact(%s)', $this->util->stringify($this->value)); + } + + return $this->string; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use SebastianBergmann\Comparator\ComparisonFailure; +use Prophecy\Comparator\Factory as ComparatorFactory; +use Prophecy\Util\StringUtil; + +/** + * Object state-checker token. + * + * @author Konstantin Kudryashov + */ +class ObjectStateToken implements TokenInterface +{ + private $name; + private $value; + private $util; + private $comparatorFactory; + + /** + * Initializes token. + * + * @param string $methodName + * @param mixed $value Expected return value + * @param null|StringUtil $util + * @param ComparatorFactory $comparatorFactory + */ + public function __construct( + $methodName, + $value, + StringUtil $util = null, + ComparatorFactory $comparatorFactory = null + ) { + $this->name = $methodName; + $this->value = $value; + $this->util = $util ?: new StringUtil; + + $this->comparatorFactory = $comparatorFactory ?: ComparatorFactory::getInstance(); + } + + /** + * Scores 8 if argument is an object, which method returns expected value. + * + * @param mixed $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + if (is_object($argument) && method_exists($argument, $this->name)) { + $actual = call_user_func(array($argument, $this->name)); + + $comparator = $this->comparatorFactory->getComparatorFor( + $actual, $this->value + ); + + try { + $comparator->assertEquals($actual, $this->value); + return 8; + } catch (ComparisonFailure $failure) { + return false; + } + } + + if (is_object($argument) && property_exists($argument, $this->name)) { + return $argument->{$this->name} === $this->value ? 8 : false; + } + + return false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('state(%s(), %s)', + $this->name, + $this->util->stringify($this->value) + ); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Callback-verified token. + * + * @author Konstantin Kudryashov + */ +class CallbackToken implements TokenInterface +{ + private $callback; + + /** + * Initializes token. + * + * @param callable $callback + * + * @throws \Prophecy\Exception\InvalidArgumentException + */ + public function __construct($callback) + { + if (!is_callable($callback)) { + throw new InvalidArgumentException(sprintf( + 'Callable expected as an argument to CallbackToken, but got %s.', + gettype($callback) + )); + } + + $this->callback = $callback; + } + + /** + * Scores 7 if callback returns true, false otherwise. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return call_user_func($this->callback, $argument) ? 7 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return 'callback()'; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Util\StringUtil; + +/** + * Identical value token. + * + * @author Florian Voutzinos + */ +class IdenticalValueToken implements TokenInterface +{ + private $value; + private $string; + private $util; + + /** + * Initializes token. + * + * @param mixed $value + * @param StringUtil $util + */ + public function __construct($value, StringUtil $util = null) + { + $this->value = $value; + $this->util = $util ?: new StringUtil(); + } + + /** + * Scores 11 if argument matches preset value. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return $argument === $this->value ? 11 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + if (null === $this->string) { + $this->string = sprintf('identical(%s)', $this->util->stringify($this->value)); + } + + return $this->string; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +/** + * Logical NOT token. + * + * @author Boris Mikhaylov + */ +class LogicalNotToken implements TokenInterface +{ + /** @var \Prophecy\Argument\Token\TokenInterface */ + private $token; + + /** + * @param mixed $value exact value or token + */ + public function __construct($value) + { + $this->token = $value instanceof TokenInterface? $value : new ExactValueToken($value); + } + + /** + * Scores 4 when preset token does not match the argument. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + return false === $this->token->scoreArgument($argument) ? 4 : false; + } + + /** + * Returns true if preset token is last. + * + * @return bool|int + */ + public function isLast() + { + return $this->token->isLast(); + } + + /** + * Returns originating token. + * + * @return TokenInterface + */ + public function getOriginatingToken() + { + return $this->token; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('not(%s)', $this->token); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Argument\Token; + +use Prophecy\Exception\InvalidArgumentException; + +/** + * Value type token. + * + * @author Konstantin Kudryashov + */ +class TypeToken implements TokenInterface +{ + private $type; + + /** + * @param string $type + */ + public function __construct($type) + { + $checker = "is_{$type}"; + if (!function_exists($checker) && !interface_exists($type) && !class_exists($type)) { + throw new InvalidArgumentException(sprintf( + 'Type or class name expected as an argument to TypeToken, but got %s.', $type + )); + } + + $this->type = $type; + } + + /** + * Scores 5 if argument has the same type this token was constructed with. + * + * @param $argument + * + * @return bool|int + */ + public function scoreArgument($argument) + { + $checker = "is_{$this->type}"; + if (function_exists($checker)) { + return call_user_func($checker, $argument) ? 5 : false; + } + + return $argument instanceof $this->type ? 5 : false; + } + + /** + * Returns false. + * + * @return bool + */ + public function isLast() + { + return false; + } + + /** + * Returns string representation for token. + * + * @return string + */ + public function __toString() + { + return sprintf('type(%s)', $this->type); + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Exporting utility. + * + * This class is derived from the PHPUnit testing framework. + * + * @author Sebastiaan Stok + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License> + */ +class ExportUtil +{ + /** + * Exports a value into a string. + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - true is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value The value to export + * @param integer $indentation The indentation level of the 2nd+ line + * + * @return string + */ + public static function export($value, $indentation = 0) + { + return static::recursiveExport($value, $indentation); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param object $object + * + * @return array + */ + public static function toArray($object) + { + $array = array(); + + foreach ((array) $object as $key => $value) { + // properties are transformed to keys in the following way: + + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + + if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + + $array[$key] = $value; + } + + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection + // Format the output similarly to print_r() in this case + if ($object instanceof SplObjectStorage) { + foreach ($object as $key => $value) { + $array[spl_object_hash($value)] = array( + 'obj' => $value, + 'inf' => $object->getInfo(), + ); + } + } + + return $array; + } + + /** + * Recursive implementation of export. + * + * @param mixed $value The value to export + * @param integer $indentation The indentation level of the 2nd+ line + * @param array $processedObjects Contains all objects that were already + * rendered + * + * @return string + */ + protected static function recursiveExport($value, $indentation, &$processedObjects = array()) + { + if ($value === null) { + return 'null'; + } + + if ($value === true) { + return 'true'; + } + + if ($value === false) { + return 'false'; + } + + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + + return "'" . str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . "'"; + } + + $origValue = $value; + + if (is_object($value)) { + if ($value instanceof ProphecyInterface) { + return sprintf('%s Object (*Prophecy*)', get_class($value)); + } elseif (in_array($value, $processedObjects, true)) { + return sprintf('%s Object (*RECURSION*)', get_class($value)); + } + + $processedObjects[] = $value; + + // Convert object to array + $value = self::toArray($value); + } + + if (is_array($value)) { + $whitespace = str_repeat(' ', $indentation); + + // There seems to be no other way to check arrays for recursion + // http://www.php.net/manual/en/language.types.array.php#73936 + preg_match_all('/\n \[(\w+)\] => Array\s+\*RECURSION\*/', print_r($value, true), $matches); + $recursiveKeys = array_unique($matches[1]); + + // Convert to valid array keys + // Numeric integer strings are automatically converted to integers + // by PHP + foreach ($recursiveKeys as $key => $recursiveKey) { + if ((string) (integer) $recursiveKey === $recursiveKey) { + $recursiveKeys[$key] = (integer) $recursiveKey; + } + } + + $content = ''; + + foreach ($value as $key => $val) { + if (in_array($key, $recursiveKeys, true)) { + $val = 'Array (*RECURSION*)'; + } else { + $val = self::recursiveExport($val, $indentation + 1, $processedObjects); + } + + $content .= $whitespace . ' ' . self::export($key) . ' => ' . $val . "\n"; + } + + if (strlen($content) > 0) { + $content = "\n" . $content . $whitespace; + } + + return sprintf( + "%s (%s)", + is_object($origValue) ? sprintf('%s:%s', get_class($origValue), spl_object_hash($origValue)) . ' Object' : 'Array', $content + ); + } + + if (is_double($value) && (double)(integer) $value === $value) { + return $value . '.0'; + } + + return (string) $value; + } +} + + * Marcello Duarte + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Prophecy\Util; + +use Prophecy\Call\Call; + +/** + * String utility. + * + * @author Konstantin Kudryashov + */ +class StringUtil +{ + /** + * Stringifies any provided value. + * + * @param mixed $value + * @param boolean $exportObject + * + * @return string + */ + public function stringify($value, $exportObject = true) + { + if (is_array($value)) { + if (range(0, count($value) - 1) === array_keys($value)) { + return '['.implode(', ', array_map(array($this, __FUNCTION__), $value)).']'; + } + + $stringify = array($this, __FUNCTION__); + + return '['.implode(', ', array_map(function ($item, $key) use ($stringify) { + return (is_integer($key) ? $key : '"'.$key.'"'). + ' => '.call_user_func($stringify, $item); + }, $value, array_keys($value))).']'; + } + if (is_resource($value)) { + return get_resource_type($value).':'.$value; + } + if (is_object($value)) { + return $exportObject ? ExportUtil::export($value) : sprintf('%s:%s', get_class($value), spl_object_hash($value)); + } + if (true === $value || false === $value) { + return $value ? 'true' : 'false'; + } + if (is_string($value)) { + $str = sprintf('"%s"', str_replace("\n", '\\n', $value)); + + if (50 <= strlen($str)) { + return substr($str, 0, 50).'"...'; + } + + return $str; + } + if (null === $value) { + return 'null'; + } + + return (string) $value; + } + + /** + * Stringifies provided array of calls. + * + * @param Call[] $calls Array of Call instances + * + * @return string + */ + public function stringifyCalls(array $calls) + { + $self = $this; + + return implode(PHP_EOL, array_map(function (Call $call) use ($self) { + return sprintf(' - %s(%s) @ %s', + $call->getMethodName(), + implode(', ', array_map(array($self, 'stringify'), $call->getArguments())), + str_replace(GETCWD().DIRECTORY_SEPARATOR, '', $call->getCallPlace()) + ); + }, $calls)); + } +} +Exporter + +Copyright (c) 2002-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, 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. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\Exporter; + +use SebastianBergmann\RecursionContext\Context; + +/** + * A nifty utility for visualizing PHP variables. + * + * + * export(new Exception); + * + */ +class Exporter +{ + /** + * Exports a value as a string + * + * The output of this method is similar to the output of print_r(), but + * improved in various aspects: + * + * - NULL is rendered as "null" (instead of "") + * - TRUE is rendered as "true" (instead of "1") + * - FALSE is rendered as "false" (instead of "") + * - Strings are always quoted with single quotes + * - Carriage returns and newlines are normalized to \n + * - Recursion and repeated rendering is treated properly + * + * @param mixed $value + * @param int $indentation The indentation level of the 2nd+ line + * @return string + */ + public function export($value, $indentation = 0) + { + return $this->recursiveExport($value, $indentation); + } + + /** + * @param mixed $data + * @param Context $context + * @return string + */ + public function shortenedRecursiveExport(&$data, Context $context = null) + { + $result = array(); + $exporter = new self(); + + if (!$context) { + $context = new Context; + } + + $context->add($data); + + foreach ($data as $key => $value) { + if (is_array($value)) { + if ($context->contains($data[$key]) !== false) { + $result[] = '*RECURSION*'; + } + + else { + $result[] = sprintf( + 'array(%s)', + $this->shortenedRecursiveExport($data[$key], $context) + ); + } + } + + else { + $result[] = $exporter->shortenedExport($value); + } + } + + return implode(', ', $result); + } + + /** + * Exports a value into a single-line string + * + * The output of this method is similar to the output of + * SebastianBergmann\Exporter\Exporter::export. This method guarantees + * thought that the result contains now newlines. + * + * Newlines are replaced by the visible string '\n'. Contents of arrays + * and objects (if any) are replaced by '...'. + * + * @param mixed $value + * @return string + * @see SebastianBergmann\Exporter\Exporter::export + */ + public function shortenedExport($value) + { + if (is_string($value)) { + $string = $this->export($value); + + if (strlen($string) > 40) { + $string = substr($string, 0, 30) . '...' . substr($string, -7); + } + + return str_replace("\n", '\n', $string); + } + + if (is_object($value)) { + return sprintf( + '%s Object (%s)', + get_class($value), + count($this->toArray($value)) > 0 ? '...' : '' + ); + } + + if (is_array($value)) { + return sprintf( + 'Array (%s)', + count($value) > 0 ? '...' : '' + ); + } + + return $this->export($value); + } + + /** + * Converts an object to an array containing all of its private, protected + * and public properties. + * + * @param mixed $value + * @return array + */ + public function toArray($value) + { + if (!is_object($value)) { + return (array) $value; + } + + $array = array(); + + foreach ((array) $value as $key => $val) { + // properties are transformed to keys in the following way: + // private $property => "\0Classname\0property" + // protected $property => "\0*\0property" + // public $property => "property" + if (preg_match('/^\0.+\0(.+)$/', $key, $matches)) { + $key = $matches[1]; + } + + // See https://github.com/php/php-src/commit/5721132 + if ($key === "\0gcdata") { + continue; + } + + $array[$key] = $val; + } + + // Some internal classes like SplObjectStorage don't work with the + // above (fast) mechanism nor with reflection in Zend. + // Format the output similarly to print_r() in this case + if ($value instanceof \SplObjectStorage) { + // However, the fast method does work in HHVM, and exposes the + // internal implementation. Hide it again. + if (property_exists('\SplObjectStorage', '__storage')) { + unset($array['__storage']); + } elseif (property_exists('\SplObjectStorage', 'storage')) { + unset($array['storage']); + } + + if (property_exists('\SplObjectStorage', '__key')) { + unset($array['__key']); + } + + foreach ($value as $key => $val) { + $array[spl_object_hash($val)] = array( + 'obj' => $val, + 'inf' => $value->getInfo(), + ); + } + } + + return $array; + } + + /** + * Recursive implementation of export + * + * @param mixed $value The value to export + * @param int $indentation The indentation level of the 2nd+ line + * @param \SebastianBergmann\RecursionContext\Context $processed Previously processed objects + * @return string + * @see SebastianBergmann\Exporter\Exporter::export + */ + protected function recursiveExport(&$value, $indentation, $processed = null) + { + if ($value === null) { + return 'null'; + } + + if ($value === true) { + return 'true'; + } + + if ($value === false) { + return 'false'; + } + + if (is_float($value) && floatval(intval($value)) === $value) { + return "$value.0"; + } + + if (is_resource($value)) { + return sprintf( + 'resource(%d) of type (%s)', + $value, + get_resource_type($value) + ); + } + + if (is_string($value)) { + // Match for most non printable chars somewhat taking multibyte chars into account + if (preg_match('/[^\x09-\x0d\x20-\xff]/', $value)) { + return 'Binary String: 0x' . bin2hex($value); + } + + return "'" . + str_replace(array("\r\n", "\n\r", "\r"), array("\n", "\n", "\n"), $value) . + "'"; + } + + $whitespace = str_repeat(' ', 4 * $indentation); + + if (!$processed) { + $processed = new Context; + } + + if (is_array($value)) { + if (($key = $processed->contains($value)) !== false) { + return 'Array &' . $key; + } + + $key = $processed->add($value); + $values = ''; + + if (count($value) > 0) { + foreach ($value as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + $this->recursiveExport($k, $indentation), + $this->recursiveExport($value[$k], $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('Array &%s (%s)', $key, $values); + } + + if (is_object($value)) { + $class = get_class($value); + + if ($hash = $processed->contains($value)) { + return sprintf('%s Object &%s', $class, $hash); + } + + $hash = $processed->add($value); + $values = ''; + $array = $this->toArray($value); + + if (count($array) > 0) { + foreach ($array as $k => $v) { + $values .= sprintf( + '%s %s => %s' . "\n", + $whitespace, + $this->recursiveExport($k, $indentation), + $this->recursiveExport($v, $indentation + 1, $processed) + ); + } + + $values = "\n" . $values . $whitespace; + } + + return sprintf('%s Object &%s (%s)', $class, $hash, $values); + } + + return var_export($value, true); + } +} +File_Iterator + +Copyright (c) 2009-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, 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. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * FilterIterator implementation that filters files based on prefix(es) and/or + * suffix(es). Hidden files and files from hidden directories are also filtered. + * + * @since Class available since Release 1.0.0 + */ +class File_Iterator extends FilterIterator +{ + const PREFIX = 0; + const SUFFIX = 1; + + /** + * @var array + */ + protected $suffixes = array(); + + /** + * @var array + */ + protected $prefixes = array(); + + /** + * @var array + */ + protected $exclude = array(); + + /** + * @var string + */ + protected $basepath; + + /** + * @param Iterator $iterator + * @param array $suffixes + * @param array $prefixes + * @param array $exclude + * @param string $basepath + */ + public function __construct(Iterator $iterator, array $suffixes = array(), array $prefixes = array(), array $exclude = array(), $basepath = NULL) + { + $exclude = array_filter(array_map('realpath', $exclude)); + + if ($basepath !== NULL) { + $basepath = realpath($basepath); + } + + if ($basepath === FALSE) { + $basepath = NULL; + } else { + foreach ($exclude as &$_exclude) { + $_exclude = str_replace($basepath, '', $_exclude); + } + } + + $this->prefixes = $prefixes; + $this->suffixes = $suffixes; + $this->exclude = $exclude; + $this->basepath = $basepath; + + parent::__construct($iterator); + } + + /** + * @return bool + */ + public function accept() + { + $current = $this->getInnerIterator()->current(); + $filename = $current->getFilename(); + $realpath = $current->getRealPath(); + + if ($this->basepath !== NULL) { + $realpath = str_replace($this->basepath, '', $realpath); + } + + // Filter files in hidden directories. + if (preg_match('=/\.[^/]*/=', $realpath)) { + return FALSE; + } + + return $this->acceptPath($realpath) && + $this->acceptPrefix($filename) && + $this->acceptSuffix($filename); + } + + /** + * @param string $path + * @return bool + * @since Method available since Release 1.1.0 + */ + protected function acceptPath($path) + { + foreach ($this->exclude as $exclude) { + if (strpos($path, $exclude) === 0) { + return FALSE; + } + } + + return TRUE; + } + + /** + * @param string $filename + * @return bool + * @since Method available since Release 1.1.0 + */ + protected function acceptPrefix($filename) + { + return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); + } + + /** + * @param string $filename + * @return bool + * @since Method available since Release 1.1.0 + */ + protected function acceptSuffix($filename) + { + return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); + } + + /** + * @param string $filename + * @param array $subString + * @param int $type + * @return bool + * @since Method available since Release 1.1.0 + */ + protected function acceptSubString($filename, array $subStrings, $type) + { + if (empty($subStrings)) { + return TRUE; + } + + $matched = FALSE; + + foreach ($subStrings as $string) { + if (($type == self::PREFIX && strpos($filename, $string) === 0) || + ($type == self::SUFFIX && + substr($filename, -1 * strlen($string)) == $string)) { + $matched = TRUE; + break; + } + } + + return $matched; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Façade implementation that uses File_Iterator_Factory to create a + * File_Iterator that operates on an AppendIterator that contains an + * RecursiveDirectoryIterator for each given path. The list of unique + * files is returned as an array. + * + * @since Class available since Release 1.3.0 + */ +class File_Iterator_Facade +{ + /** + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes + * @param array $exclude + * @param bool $commonPath + * @return array + */ + public function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = array(), $commonPath = FALSE) + { + if (is_string($paths)) { + $paths = array($paths); + } + + $factory = new File_Iterator_Factory; + $iterator = $factory->getFileIterator( + $paths, $suffixes, $prefixes, $exclude + ); + + $files = array(); + + foreach ($iterator as $file) { + $file = $file->getRealPath(); + + if ($file) { + $files[] = $file; + } + } + + foreach ($paths as $path) { + if (is_file($path)) { + $files[] = realpath($path); + } + } + + $files = array_unique($files); + sort($files); + + if ($commonPath) { + return array( + 'commonPath' => $this->getCommonPath($files), + 'files' => $files + ); + } else { + return $files; + } + } + + /** + * Returns the common path of a set of files. + * + * @param array $files + * @return string + */ + protected function getCommonPath(array $files) + { + $count = count($files); + + if ($count == 0) { + return ''; + } + + if ($count == 1) { + return dirname($files[0]) . DIRECTORY_SEPARATOR; + } + + $_files = array(); + + foreach ($files as $file) { + $_files[] = $_fileParts = explode(DIRECTORY_SEPARATOR, $file); + + if (empty($_fileParts[0])) { + $_fileParts[0] = DIRECTORY_SEPARATOR; + } + } + + $common = ''; + $done = FALSE; + $j = 0; + $count--; + + while (!$done) { + for ($i = 0; $i < $count; $i++) { + if ($_files[$i][$j] != $_files[$i+1][$j]) { + $done = TRUE; + break; + } + } + + if (!$done) { + $common .= $_files[0][$j]; + + if ($j > 0) { + $common .= DIRECTORY_SEPARATOR; + } + } + + $j++; + } + + return DIRECTORY_SEPARATOR . $common; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Factory Method implementation that creates a File_Iterator that operates on + * an AppendIterator that contains an RecursiveDirectoryIterator for each given + * path. + * + * @since Class available since Release 1.1.0 + */ +class File_Iterator_Factory +{ + /** + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes + * @param array $exclude + * @return AppendIterator + */ + public function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = array()) + { + if (is_string($paths)) { + $paths = array($paths); + } + + $paths = $this->getPathsAfterResolvingWildcards($paths); + $exclude = $this->getPathsAfterResolvingWildcards($exclude); + + if (is_string($prefixes)) { + if ($prefixes != '') { + $prefixes = array($prefixes); + } else { + $prefixes = array(); + } + } + + if (is_string($suffixes)) { + if ($suffixes != '') { + $suffixes = array($suffixes); + } else { + $suffixes = array(); + } + } + + $iterator = new AppendIterator; + + foreach ($paths as $path) { + if (is_dir($path)) { + $iterator->append( + new File_Iterator( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::FOLLOW_SYMLINKS) + ), + $suffixes, + $prefixes, + $exclude, + $path + ) + ); + } + } + + return $iterator; + } + + /** + * @param array $paths + * @return array + */ + protected function getPathsAfterResolvingWildcards(array $paths) + { + $_paths = array(); + + foreach ($paths as $path) { + if ($locals = glob($path, GLOB_ONLYDIR)) { + $_paths = array_merge($_paths, $locals); + } else { + $_paths[] = $path; + } + } + + return $_paths; + } +} +phpunit/phpunit: 4.8.6 +doctrine/instantiator: 1.0.5 +phpdocumentor/reflection-docblock: 2.0.4 +phpspec/prophecy: v1.5.0 +phpunit/dbunit: 1.4.1 +phpunit/php-code-coverage: 2.2.2 +phpunit/php-file-iterator: 1.4.1 +phpunit/php-invoker: 1.1.4 +phpunit/php-text-template: 1.2.1 +phpunit/php-timer: 1.0.7 +phpunit/php-token-stream: 1.4.6 +phpunit/phpunit-mock-objects: 2.3.7 +phpunit/phpunit-selenium: 1.4.2 +sebastian/comparator: 1.2.0 +sebastian/diff: 1.3.0 +sebastian/environment: 1.3.2 +sebastian/exporter: 1.2.1 +sebastian/global-state: 1.0.0 +sebastian/recursion-context: 1.0.1 +sebastian/version: 1.0.6 +symfony/yaml: v2.7.3 + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + */ +interface Exception +{ +} +Recursion Context + +Copyright (c) 2002-2015, Sebastian Bergmann . +All rights reserved. + +Redistribution and use in source and binary forms, 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. + + * Neither the name of Sebastian Bergmann nor the names of his + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + * A context containing previously processed arrays and objects + * when recursively processing a value. + */ +final class Context +{ + /** + * @var array[] + */ + private $arrays; + + /** + * @var \SplObjectStorage + */ + private $objects; + + /** + * Initialises the context + */ + public function __construct() + { + $this->arrays = array(); + $this->objects = new \SplObjectStorage; + } + + /** + * Adds a value to the context. + * + * @param array|object $value The value to add. + * @return int|string The ID of the stored value, either as + * a string or integer. + * @throws InvalidArgumentException Thrown if $value is not an array or + * object + */ + public function add(&$value) + { + if (is_array($value)) { + return $this->addArray($value); + } + + else if (is_object($value)) { + return $this->addObject($value); + } + + throw new InvalidArgumentException( + 'Only arrays and objects are supported' + ); + } + + /** + * Checks if the given value exists within the context. + * + * @param array|object $value The value to check. + * @return int|string|false The string or integer ID of the stored + * value if it has already been seen, or + * false if the value is not stored. + * @throws InvalidArgumentException Thrown if $value is not an array or + * object + */ + public function contains(&$value) + { + if (is_array($value)) { + return $this->containsArray($value); + } + + else if (is_object($value)) { + return $this->containsObject($value); + } + + throw new InvalidArgumentException( + 'Only arrays and objects are supported' + ); + } + + /** + * @param array $array + * @return bool|int + */ + private function addArray(array &$array) + { + $key = $this->containsArray($array); + + if ($key !== false) { + return $key; + } + + $this->arrays[] = &$array; + + return count($this->arrays) - 1; + } + + /** + * @param object $object + * @return string + */ + private function addObject($object) + { + if (!$this->objects->contains($object)) { + $this->objects->attach($object); + } + + return spl_object_hash($object); + } + + /** + * @param array $array + * @return int|false + */ + private function containsArray(array &$array) + { + $keys = array_keys($this->arrays, $array, true); + $hash = '_Key_' . hash('sha512', microtime(true)); + + foreach ($keys as $key) { + $this->arrays[$key][$hash] = $hash; + + if (isset($array[$hash]) && $array[$hash] === $hash) { + unset($this->arrays[$key][$hash]); + + return $key; + } + + unset($this->arrays[$key][$hash]); + } + + return false; + } + + /** + * @param object $value + * @return string|false + */ + private function containsObject($value) + { + if ($this->objects->contains($value)) { + return spl_object_hash($value); + } + + return false; + } +} + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace SebastianBergmann\RecursionContext; + +/** + */ +final class InvalidArgumentException extends \InvalidArgumentException implements Exception +{ +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Sebastian Bergmann + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.0.0 + */ + +/** + * TestCase class that uses Selenium to provide + * the functionality required for web testing. + * + * @package PHPUnit_Selenium + * @author Sebastian Bergmann + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.0.0 + * + * @method unknown addLocationStrategy() + * @method unknown addLocationStrategyAndWait() + * @method unknown addScript() + * @method unknown addScriptAndWait() + * @method unknown addSelection() + * @method unknown addSelectionAndWait() + * @method unknown allowNativeXpath() + * @method unknown allowNativeXpathAndWait() + * @method unknown altKeyDown() + * @method unknown altKeyDownAndWait() + * @method unknown altKeyUp() + * @method unknown altKeyUpAndWait() + * @method unknown answerOnNextPrompt() + * @method unknown assignId() + * @method unknown assignIdAndWait() + * @method unknown assertAlert + * @method unknown assertAlertNotPresent + * @method unknown assertAlertPresent + * @method unknown assertAllButtons + * @method unknown assertAllFields + * @method unknown assertAllLinks + * @method unknown assertAllWindowIds + * @method unknown assertAllWindowNames + * @method unknown assertAllWindowTitles + * @method unknown assertAttribute + * @method unknown assertAttributeFromAllWindows + * @method unknown assertBodyText + * @method unknown assertChecked + * @method unknown assertConfirmation + * @method unknown assertConfirmationNotPresent + * @method unknown assertConfirmationPresent + * @method unknown assertCookie + * @method unknown assertCookieByName + * @method unknown assertCookieNotPresent + * @method unknown assertCookiePresent + * @method unknown assertCssCount + * @method unknown assertCursorPosition + * @method unknown assertEditable + * @method unknown assertElementHeight + * @method unknown assertElementIndex + * @method unknown assertElementNotPresent + * @method unknown assertElementPositionLeft + * @method unknown assertElementPositionTop + * @method unknown assertElementPresent + * @method unknown assertElementWidth + * @method unknown assertEval + * @method unknown assertExpression + * @method unknown assertHtmlSource + * @method unknown assertLocation + * @method unknown assertLogMessages + * @method unknown assertMouseSpeed + * @method unknown assertNotAlert + * @method unknown assertNotAllButtons + * @method unknown assertNotAllFields + * @method unknown assertNotAllLinks + * @method unknown assertNotAllWindowIds + * @method unknown assertNotAllWindowNames + * @method unknown assertNotAllWindowTitles + * @method unknown assertNotAttribute + * @method unknown assertNotAttributeFromAllWindows + * @method unknown assertNotBodyText + * @method unknown assertNotChecked + * @method unknown assertNotConfirmation + * @method unknown assertNotCookie + * @method unknown assertNotCookieByName + * @method unknown assertNotCssCount + * @method unknown assertNotCursorPosition + * @method unknown assertNotEditable + * @method unknown assertNotElementHeight + * @method unknown assertNotElementIndex + * @method unknown assertNotElementPositionLeft + * @method unknown assertNotElementPositionTop + * @method unknown assertNotElementWidth + * @method unknown assertNotEval + * @method unknown assertNotExpression + * @method unknown assertNotHtmlSource + * @method unknown assertNotLocation + * @method unknown assertNotLogMessages + * @method unknown assertNotMouseSpeed + * @method unknown assertNotOrdered + * @method unknown assertNotPrompt + * @method unknown assertNotSelectOptions + * @method unknown assertNotSelectedId + * @method unknown assertNotSelectedIds + * @method unknown assertNotSelectedIndex + * @method unknown assertNotSelectedIndexes + * @method unknown assertNotSelectedLabel + * @method unknown assertNotSelectedLabels + * @method unknown assertNotSelectedValue + * @method unknown assertNotSelectedValues + * @method unknown assertNotSomethingSelected + * @method unknown assertNotSpeed + * @method unknown assertNotSpeedAndWait + * @method unknown assertNotTable + * @method unknown assertNotText + * @method unknown assertNotTitle + * @method unknown assertNotValue + * @method unknown assertNotVisible + * @method unknown assertNotWhetherThisFrameMatchFrameExpression + * @method unknown assertNotWhetherThisWindowMatchWindowExpression + * @method unknown assertNotXpathCount + * @method unknown assertOrdered + * @method unknown assertPrompt + * @method unknown assertPromptNotPresent + * @method unknown assertPromptPresent + * @method unknown assertSelectOptions + * @method unknown assertSelectedId + * @method unknown assertSelectedIds + * @method unknown assertSelectedIndex + * @method unknown assertSelectedIndexes + * @method unknown assertSelectedLabel + * @method unknown assertSelectedLabels + * @method unknown assertSelectedValue + * @method unknown assertSelectedValues + * @method unknown assertSomethingSelected + * @method unknown assertSpeed + * @method unknown assertSpeedAndWait + * @method unknown assertTable + * @method unknown assertText + * @method unknown assertTextNotPresent + * @method unknown assertTextPresent + * @method unknown assertTitle + * @method unknown assertValue + * @method unknown assertVisible + * @method unknown assertWhetherThisFrameMatchFrameExpression + * @method unknown assertWhetherThisWindowMatchWindowExpression + * @method unknown assertXpathCount + * @method unknown attachFile() + * @method unknown break() + * @method unknown captureEntirePageScreenshot() + * @method unknown captureEntirePageScreenshotAndWait() + * @method unknown captureEntirePageScreenshotToStringAndWait() + * @method unknown captureScreenshotAndWait() + * @method unknown captureScreenshotToStringAndWait() + * @method unknown check() + * @method unknown checkAndWait() + * @method unknown chooseCancelOnNextConfirmation() + * @method unknown chooseCancelOnNextConfirmationAndWait() + * @method unknown chooseOkOnNextConfirmation() + * @method unknown chooseOkOnNextConfirmationAndWait() + * @method unknown click() + * @method unknown clickAndWait() + * @method unknown clickAt() + * @method unknown clickAtAndWait() + * @method unknown close() + * @method unknown contextMenu() + * @method unknown contextMenuAndWait() + * @method unknown contextMenuAt() + * @method unknown contextMenuAtAndWait() + * @method unknown controlKeyDown() + * @method unknown controlKeyDownAndWait() + * @method unknown controlKeyUp() + * @method unknown controlKeyUpAndWait() + * @method unknown createCookie() + * @method unknown createCookieAndWait() + * @method unknown deleteAllVisibleCookies() + * @method unknown deleteAllVisibleCookiesAndWait() + * @method unknown deleteCookie() + * @method unknown deleteCookieAndWait() + * @method unknown deselectPopUp() + * @method unknown deselectPopUpAndWait() + * @method unknown doubleClick() + * @method unknown doubleClickAndWait() + * @method unknown doubleClickAt() + * @method unknown doubleClickAtAndWait() + * @method unknown dragAndDrop() + * @method unknown dragAndDropAndWait() + * @method unknown dragAndDropToObject() + * @method unknown dragAndDropToObjectAndWait() + * @method unknown dragDrop() + * @method unknown dragDropAndWait() + * @method unknown echo() + * @method unknown fireEvent() + * @method unknown fireEventAndWait() + * @method unknown focus() + * @method unknown focusAndWait() + * @method string getAlert() + * @method array getAllButtons() + * @method array getAllFields() + * @method array getAllLinks() + * @method array getAllWindowIds() + * @method array getAllWindowNames() + * @method array getAllWindowTitles() + * @method string getAttribute() + * @method array getAttributeFromAllWindows() + * @method string getBodyText() + * @method string getConfirmation() + * @method string getCookie() + * @method string getCookieByName() + * @method integer getCursorPosition() + * @method integer getElementHeight() + * @method integer getElementIndex() + * @method integer getElementPositionLeft() + * @method integer getElementPositionTop() + * @method integer getElementWidth() + * @method string getEval() + * @method string getExpression() + * @method string getHtmlSource() + * @method string getLocation() + * @method string getLogMessages() + * @method integer getMouseSpeed() + * @method string getPrompt() + * @method array getSelectOptions() + * @method string getSelectedId() + * @method array getSelectedIds() + * @method string getSelectedIndex() + * @method array getSelectedIndexes() + * @method string getSelectedLabel() + * @method array getSelectedLabels() + * @method string getSelectedValue() + * @method array getSelectedValues() + * @method unknown getSpeed() + * @method unknown getSpeedAndWait() + * @method string getTable() + * @method string getText() + * @method string getTitle() + * @method string getValue() + * @method boolean getWhetherThisFrameMatchFrameExpression() + * @method boolean getWhetherThisWindowMatchWindowExpression() + * @method integer getXpathCount() + * @method unknown goBack() + * @method unknown goBackAndWait() + * @method unknown highlight() + * @method unknown highlightAndWait() + * @method unknown ignoreAttributesWithoutValue() + * @method unknown ignoreAttributesWithoutValueAndWait() + * @method boolean isAlertPresent() + * @method boolean isChecked() + * @method boolean isConfirmationPresent() + * @method boolean isCookiePresent() + * @method boolean isEditable() + * @method boolean isElementPresent() + * @method boolean isOrdered() + * @method boolean isPromptPresent() + * @method boolean isSomethingSelected() + * @method boolean isTextPresent() + * @method boolean isVisible() + * @method unknown keyDown() + * @method unknown keyDownAndWait() + * @method unknown keyDownNative() + * @method unknown keyDownNativeAndWait() + * @method unknown keyPress() + * @method unknown keyPressAndWait() + * @method unknown keyPressNative() + * @method unknown keyPressNativeAndWait() + * @method unknown keyUp() + * @method unknown keyUpAndWait() + * @method unknown keyUpNative() + * @method unknown keyUpNativeAndWait() + * @method unknown metaKeyDown() + * @method unknown metaKeyDownAndWait() + * @method unknown metaKeyUp() + * @method unknown metaKeyUpAndWait() + * @method unknown mouseDown() + * @method unknown mouseDownAndWait() + * @method unknown mouseDownAt() + * @method unknown mouseDownAtAndWait() + * @method unknown mouseMove() + * @method unknown mouseMoveAndWait() + * @method unknown mouseMoveAt() + * @method unknown mouseMoveAtAndWait() + * @method unknown mouseOut() + * @method unknown mouseOutAndWait() + * @method unknown mouseOver() + * @method unknown mouseOverAndWait() + * @method unknown mouseUp() + * @method unknown mouseUpAndWait() + * @method unknown mouseUpAt() + * @method unknown mouseUpAtAndWait() + * @method unknown mouseUpRight() + * @method unknown mouseUpRightAndWait() + * @method unknown mouseUpRightAt() + * @method unknown mouseUpRightAtAndWait() + * @method unknown open() + * @method unknown openWindow() + * @method unknown openWindowAndWait() + * @method unknown pause() + * @method unknown refresh() + * @method unknown refreshAndWait() + * @method unknown removeAllSelections() + * @method unknown removeAllSelectionsAndWait() + * @method unknown removeScript() + * @method unknown removeScriptAndWait() + * @method unknown removeSelection() + * @method unknown removeSelectionAndWait() + * @method unknown retrieveLastRemoteControlLogs() + * @method unknown rollup() + * @method unknown rollupAndWait() + * @method unknown runScript() + * @method unknown runScriptAndWait() + * @method unknown select() + * @method unknown selectAndWait() + * @method unknown selectFrame() + * @method unknown selectPopUp() + * @method unknown selectPopUpAndWait() + * @method unknown selectWindow() + * @method unknown setBrowserLogLevel() + * @method unknown setBrowserLogLevelAndWait() + * @method unknown setContext() + * @method unknown setCursorPosition() + * @method unknown setCursorPositionAndWait() + * @method unknown setMouseSpeed() + * @method unknown setMouseSpeedAndWait() + * @method unknown setSpeed() + * @method unknown setSpeedAndWait() + * @method unknown shiftKeyDown() + * @method unknown shiftKeyDownAndWait() + * @method unknown shiftKeyUp() + * @method unknown shiftKeyUpAndWait() + * @method unknown shutDownSeleniumServer() + * @method unknown store() + * @method unknown submit() + * @method unknown submitAndWait() + * @method unknown type() + * @method unknown typeAndWait() + * @method unknown typeKeys() + * @method unknown typeKeysAndWait() + * @method unknown uncheck() + * @method unknown uncheckAndWait() + * @method unknown useXpathLibrary() + * @method unknown useXpathLibraryAndWait() + * @method unknown waitForAlert + * @method unknown waitForAlertNotPresent + * @method unknown waitForAlertPresent + * @method unknown waitForAllButtons + * @method unknown waitForAllFields + * @method unknown waitForAllLinks + * @method unknown waitForAllWindowIds + * @method unknown waitForAllWindowNames + * @method unknown waitForAllWindowTitles + * @method unknown waitForAttribute + * @method unknown waitForAttributeFromAllWindows + * @method unknown waitForBodyText + * @method unknown waitForChecked + * @method unknown waitForCondition() + * @method unknown waitForConfirmation + * @method unknown waitForConfirmationNotPresent + * @method unknown waitForConfirmationPresent + * @method unknown waitForCookie + * @method unknown waitForCookieByName + * @method unknown waitForCookieNotPresent + * @method unknown waitForCookiePresent + * @method unknown waitForCssCount + * @method unknown waitForCursorPosition + * @method unknown waitForEditable + * @method unknown waitForElementHeight + * @method unknown waitForElementIndex + * @method unknown waitForElementNotPresent + * @method unknown waitForElementPositionLeft + * @method unknown waitForElementPositionTop + * @method unknown waitForElementPresent + * @method unknown waitForElementWidth + * @method unknown waitForEval + * @method unknown waitForExpression + * @method unknown waitForHtmlSource + * @method unknown waitForLocation + * @method unknown waitForLogMessages + * @method unknown waitForMouseSpeed + * @method unknown waitForNotAlert + * @method unknown waitForNotAllButtons + * @method unknown waitForNotAllFields + * @method unknown waitForNotAllLinks + * @method unknown waitForNotAllWindowIds + * @method unknown waitForNotAllWindowNames + * @method unknown waitForNotAllWindowTitles + * @method unknown waitForNotAttribute + * @method unknown waitForNotAttributeFromAllWindows + * @method unknown waitForNotBodyText + * @method unknown waitForNotChecked + * @method unknown waitForNotConfirmation + * @method unknown waitForNotCookie + * @method unknown waitForNotCookieByName + * @method unknown waitForNotCssCount + * @method unknown waitForNotCursorPosition + * @method unknown waitForNotEditable + * @method unknown waitForNotElementHeight + * @method unknown waitForNotElementIndex + * @method unknown waitForNotElementPositionLeft + * @method unknown waitForNotElementPositionTop + * @method unknown waitForNotElementWidth + * @method unknown waitForNotEval + * @method unknown waitForNotExpression + * @method unknown waitForNotHtmlSource + * @method unknown waitForNotLocation + * @method unknown waitForNotLogMessages + * @method unknown waitForNotMouseSpeed + * @method unknown waitForNotOrdered + * @method unknown waitForNotPrompt + * @method unknown waitForNotSelectOptions + * @method unknown waitForNotSelectedId + * @method unknown waitForNotSelectedIds + * @method unknown waitForNotSelectedIndex + * @method unknown waitForNotSelectedIndexes + * @method unknown waitForNotSelectedLabel + * @method unknown waitForNotSelectedLabels + * @method unknown waitForNotSelectedValue + * @method unknown waitForNotSelectedValues + * @method unknown waitForNotSomethingSelected + * @method unknown waitForNotSpeed + * @method unknown waitForNotSpeedAndWait + * @method unknown waitForNotTable + * @method unknown waitForNotText + * @method unknown waitForNotTitle + * @method unknown waitForNotValue + * @method unknown waitForNotVisible + * @method unknown waitForNotWhetherThisFrameMatchFrameExpression + * @method unknown waitForNotWhetherThisWindowMatchWindowExpression + * @method unknown waitForNotXpathCount + * @method unknown waitForOrdered + * @method unknown waitForPageToLoad() + * @method unknown waitForPopUp() + * @method unknown waitForPrompt + * @method unknown waitForPromptNotPresent + * @method unknown waitForPromptPresent + * @method unknown waitForSelectOptions + * @method unknown waitForSelectedId + * @method unknown waitForSelectedIds + * @method unknown waitForSelectedIndex + * @method unknown waitForSelectedIndexes + * @method unknown waitForSelectedLabel + * @method unknown waitForSelectedLabels + * @method unknown waitForSelectedValue + * @method unknown waitForSelectedValues + * @method unknown waitForSomethingSelected + * @method unknown waitForSpeed + * @method unknown waitForSpeedAndWait + * @method unknown waitForTable + * @method unknown waitForText + * @method unknown waitForTextNotPresent + * @method unknown waitForTextPresent + * @method unknown waitForTitle + * @method unknown waitForValue + * @method unknown waitForVisible + * @method unknown waitForWhetherThisFrameMatchFrameExpression + * @method unknown waitForWhetherThisWindowMatchWindowExpression + * @method unknown waitForXpathCount + * @method unknown windowFocus() + * @method unknown windowMaximize() + */ +abstract class PHPUnit_Extensions_SeleniumTestCase extends PHPUnit_Framework_TestCase +{ + /** + * @var array + */ + public static $browsers = array(); + + /** + * @var string + */ + protected $browserName; + + /** + * @var boolean + */ + protected $collectCodeCoverageInformation = FALSE; + + /** + * @var string + */ + protected $coverageScriptUrl = ''; + + /** + * @var PHPUnit_Extensions_SeleniumTestCase_Driver[] + */ + protected $drivers = array(); + + /** + * @var boolean + */ + protected $inDefaultAssertions = FALSE; + + /** + * @var string + */ + protected $testId; + + /** + * @var array + * @access protected + */ + protected $verificationErrors = array(); + + /** + * @var boolean + */ + protected $captureScreenshotOnFailure = FALSE; + + /** + * @var string + */ + protected $screenshotPath = ''; + + /** + * @var string + */ + protected $screenshotUrl = ''; + + /** + * @var string + */ + protected $screenshotBgColor = ''; + + /** + * @var integer the number of seconds to wait before declaring + * the Selenium server not reachable + */ + protected $serverConnectionTimeOut = 10; + + /** + * @var boolean + */ + private $serverRunning; + + /** + * @var boolean + */ + private static $shareSession; + + /** + * The last sessionId used for running a test. + * @var string + */ + private static $sessionId; + + /** + * @param boolean + */ + public static function shareSession($shareSession) + { + self::$shareSession = $shareSession; + } + + /** + * @param string $name + * @param array $data + * @param string $dataName + * @param array $browser + * @throws InvalidArgumentException + */ + public function __construct($name = NULL, array $data = array(), $dataName = '', array $browser = array()) + { + parent::__construct($name, $data, $dataName); + $this->testId = md5(uniqid(rand(), TRUE)); + $this->getDriver($browser); + } + + public function setupSpecificBrowser(array $browser) + { + $this->getDriver($browser); + } + + /** + * Stops any shared session still open at the end of the current + * PHPUnit process. + */ + public function __destruct() + { + $this->stopSession(); + } + + /** + * @param string $className + * @return PHPUnit_Framework_TestSuite + */ + public static function suite($className) + { + return PHPUnit_Extensions_SeleniumTestSuite::fromTestCaseClass($className); + } + + /** + * Runs the test case and collects the results in a TestResult object. + * If no TestResult object is passed a new one will be created. + * + * @param PHPUnit_Framework_TestResult $result + * @return PHPUnit_Framework_TestResult + * @throws InvalidArgumentException + */ + public function run(PHPUnit_Framework_TestResult $result = NULL) + { + if ($result === NULL) { + $result = $this->createResult(); + } + + $this->collectCodeCoverageInformation = $result->getCollectCodeCoverageInformation(); + + foreach ($this->drivers as $driver) { + $driver->setCollectCodeCoverageInformation( + $this->collectCodeCoverageInformation + ); + } + + parent::run($result); + + if ($this->collectCodeCoverageInformation) { + $result->getCodeCoverage()->append( + $this->getCodeCoverage(), $this + ); + } + + return $result; + } + + /** + * @param array $browser + * @return PHPUnit_Extensions_SeleniumTestCase_Driver + */ + protected function getDriver(array $browser) + { + if (isset($browser['name'])) { + if (!is_string($browser['name'])) { + throw new InvalidArgumentException( + 'Array element "name" is no string.' + ); + } + } else { + $browser['name'] = ''; + } + + if (isset($browser['browser'])) { + if (!is_string($browser['browser'])) { + throw new InvalidArgumentException( + 'Array element "browser" is no string.' + ); + } + } else { + $browser['browser'] = ''; + } + + if (isset($browser['host'])) { + if (!is_string($browser['host'])) { + throw new InvalidArgumentException( + 'Array element "host" is no string.' + ); + } + } else { + $browser['host'] = 'localhost'; + } + + if (isset($browser['port'])) { + if (!is_int($browser['port'])) { + throw new InvalidArgumentException( + 'Array element "port" is no integer.' + ); + } + } else { + $browser['port'] = 4444; + } + + if (isset($browser['timeout'])) { + if (!is_int($browser['timeout'])) { + throw new InvalidArgumentException( + 'Array element "timeout" is no integer.' + ); + } + } else { + $browser['timeout'] = 30; + } + + if (isset($browser['httpTimeout'])) { + if (!is_int($browser['httpTimeout'])) { + throw new InvalidArgumentException( + 'Array element "httpTimeout" is no integer.' + ); + } + } else { + $browser['httpTimeout'] = 45; + } + + $driver = new PHPUnit_Extensions_SeleniumTestCase_Driver; + $driver->setName($browser['name']); + $driver->setBrowser($browser['browser']); + $driver->setHost($browser['host']); + $driver->setPort($browser['port']); + $driver->setTimeout($browser['timeout']); + $driver->setHttpTimeout($browser['httpTimeout']); + $driver->setTestCase($this); + $driver->setTestId($this->testId); + + $this->drivers[0] = $driver; + + return $driver; + } + + public function skipWithNoServerRunning() + { + try { + fsockopen($this->drivers[0]->getHost(), $this->drivers[0]->getPort(), $errno, $errstr, $this->serverConnectionTimeOut); + $this->serverRunning = TRUE; + } catch (PHPUnit_Framework_Error_Warning $e) { + $this->markTestSkipped( + sprintf( + 'Could not connect to the Selenium Server on %s:%d.', + $this->drivers[0]->getHost(), + $this->drivers[0]->getPort() + ) + ); + $this->serverRunning = FALSE; + } + } + + /** + * @return string + */ + protected function prepareTestSession() + { + $testCaseClassVars = get_class_vars(get_class($this)); + if ($testCaseClassVars['browsers']) { + return $this->start(); + } + if (self::$shareSession && self::$sessionId !== NULL) { + $this->setSessionId(self::$sessionId); + $this->selectWindow('null'); + } else { + self::$sessionId = $this->start(); + } + + return self::$sessionId; + } + + /** + * @throws RuntimeException + */ + protected function runTest() + { + $this->skipWithNoServerRunning(); + + $this->prepareTestSession(); + + if (!is_file($this->getName(FALSE))) { + $result = parent::runTest(); + } else { + $this->runSelenese($this->getName(FALSE)); + $result = NULL; + } + + if (!empty($this->verificationErrors)) { + $this->fail(implode("\n", $this->verificationErrors)); + } + + if (!self::$shareSession) { + $this->stopSession(); + } + + return $result; + } + + private function stopSession() + { + try { + $this->stop(); + } catch (RuntimeException $e) { } + } + + /** + * Returns a string representation of the test case. + * + * @return string + */ + public function toString() + { + $buffer = parent::toString(); + + if (!empty($this->browserName)) { + $buffer .= ' with browser ' . $this->browserName; + } + + return $buffer; + } + + /** + * Runs a test from a Selenese (HTML) specification. + * + * @param string $filename + */ + public function runSelenese($filename) + { + $document = PHPUnit_Util_XML::loadFile($filename, TRUE); + $xpath = new DOMXPath($document); + $rows = $xpath->query('body/table/tbody/tr'); + + foreach ($rows as $row) { + $action = NULL; + $arguments = array(); + $columns = $xpath->query('td', $row); + + foreach ($columns as $column) { + if ($action === NULL) { + $action = PHPUnit_Util_XML::nodeToText($column); + } else { + $arguments[] = PHPUnit_Util_XML::nodeToText($column); + } + } + + if (method_exists($this, $action)) { + call_user_func_array(array($this, $action), $arguments); + } else { + $this->__call($action, $arguments); + } + } + } + + /** + * Delegate method calls to the driver. + * + * @param string $command + * @param array $arguments + * @return mixed + */ + public function __call($command, $arguments) + { + $result = call_user_func_array( + array($this->drivers[0], $command), $arguments + ); + + $this->verificationErrors = array_merge( + $this->verificationErrors, $this->drivers[0]->getVerificationErrors() + ); + + $this->drivers[0]->clearVerificationErrors(); + + return $result; + } + + /** + * Asserts that an element's value is equal to a given string. + * + * @param string $locator + * @param string $text + * @param string $message + */ + public function assertElementValueEquals($locator, $text, $message = '') + { + $this->assertEquals($text, $this->getValue($locator), $message); + } + + /** + * Asserts that an element's value is not equal to a given string. + * + * @param string $locator + * @param string $text + * @param string $message + */ + public function assertElementValueNotEquals($locator, $text, $message = '') + { + $this->assertNotEquals($text, $this->getValue($locator), $message); + } + + /** + * Asserts that an element's value contains a given string. + * + * @param string $locator + * @param string $text + * @param string $message + */ + public function assertElementValueContains($locator, $text, $message = '') + { + $this->assertContains($text, $this->getValue($locator), $message); + } + + /** + * Asserts that an element's value does not contain a given string. + * + * @param string $locator + * @param string $text + * @param string $message + */ + public function assertElementValueNotContains($locator, $text, $message = '') + { + $this->assertNotContains($text, $this->getValue($locator), $message); + } + + /** + * Asserts that an element contains a given string. + * + * @param string $locator + * @param string $text + * @param string $message + */ + public function assertElementContainsText($locator, $text, $message = '') + { + $this->assertContains($text, $this->getText($locator), $message); + } + + /** + * Asserts that an element does not contain a given string. + * + * @param string $locator + * @param string $text + * @param string $message + */ + public function assertElementNotContainsText($locator, $text, $message = '') + { + $this->assertNotContains($text, $this->getText($locator), $message); + } + + /** + * Asserts that a select element has a specific option. + * + * @param string $selectLocator + * @param string $option + * @param string $message + */ + public function assertSelectHasOption($selectLocator, $option, $message = '') + { + $this->assertContains($option, $this->getSelectOptions($selectLocator), $message); + } + + /** + * Asserts that a select element does not have a specific option. + * + * @param string $selectLocator + * @param string $option + * @param string $message + */ + public function assertSelectNotHasOption($selectLocator, $option, $message = '') + { + $this->assertNotContains($option, $this->getSelectOptions($selectLocator), $message); + } + + /** + * Asserts that a specific label is selected. + * + * @param string $selectLocator + * @param string $value + * @param string $message + */ + public function assertSelected($selectLocator, $option, $message = '') + { + if ($message == '') { + $message = sprintf( + 'Label "%s" not selected in "%s".', + $option, + $selectLocator + ); + } + + $this->assertEquals( + $option, + $this->getSelectedLabel($selectLocator), + $message + ); + } + + /** + * Asserts that a specific label is not selected. + * + * @param string $selectLocator + * @param string $value + * @param string $message + */ + public function assertNotSelected($selectLocator, $option, $message = '') + { + if ($message == '') { + $message = sprintf( + 'Label "%s" selected in "%s".', + $option, + $selectLocator + ); + } + + $this->assertNotEquals( + $option, + $this->getSelectedLabel($selectLocator), + $message + ); + } + + /** + * Asserts that a specific value is selected. + * + * @param string $selectLocator + * @param string $value + * @param string $message + */ + public function assertIsSelected($selectLocator, $value, $message = '') + { + if ($message == '') { + $message = sprintf( + 'Value "%s" not selected in "%s".', + $value, + $selectLocator + ); + } + + $this->assertEquals( + $value, $this->getSelectedValue($selectLocator), + $message + ); + } + + /** + * Asserts that a specific value is not selected. + * + * @param string $selectLocator + * @param string $value + * @param string $message + */ + public function assertIsNotSelected($selectLocator, $value, $message = '') + { + if ($message == '') { + $message = sprintf( + 'Value "%s" selected in "%s".', + $value, + $selectLocator + ); + } + + $this->assertNotEquals( + $value, + $this->getSelectedValue($selectLocator), + $message + ); + } + + /** + * Template Method that is called after Selenium actions. + * + * @param string $action + */ + protected function defaultAssertions($action) + { + } + + /** + * @return array + */ + protected function getCodeCoverage() + { + $coverage = new PHPUnit_Extensions_SeleniumCommon_RemoteCoverage( + $this->coverageScriptUrl, + $this->testId + ); + return $coverage->get(); + } + + /** + * @param string $action + */ + public function runDefaultAssertions($action) + { + if (!$this->inDefaultAssertions) { + $this->inDefaultAssertions = TRUE; + $this->defaultAssertions($action); + $this->inDefaultAssertions = FALSE; + } + } + + /** + * This method is called when a test method did not execute successfully. + * + * @param Exception $e + */ + protected function onNotSuccessfulTest(Exception $e) + { + if (!$this->serverRunning) { + throw $e; + } + + try { + $this->restoreSessionStateAfterFailedTest(); + $buffer = ''; + + if ($this->captureScreenshotOnFailure) { + $buffer .= 'Current URL: ' . $this->drivers[0]->getLocation() . + "\n"; + + $screenshotInfo = $this->takeScreenshot(); + if ($screenshotInfo != '') { + $buffer .= $screenshotInfo; + } + } + + $this->stopSession(); + } catch (Exception $another) { + $buffer = "Issues while capturing the screenshot:\n" . $another->getMessage(); + } + + if ($e instanceof PHPUnit_Framework_ExpectationFailedException + && is_object($e->getComparisonFailure())) { + $message = $e->getComparisonFailure()->toString(); + } else { + $message = $e->getMessage(); + } + + $buffer .= "\n" . $message; + + // gain the screenshot path, lose the stack trace + if ($this->captureScreenshotOnFailure) { + throw new PHPUnit_Framework_Error($buffer, $e->getCode(), $e->getFile(), $e->getLine(), $e); + } + + // yes to stack trace and everything + if ($e instanceof PHPUnit_Framework_IncompleteTestError + || $e instanceof PHPUnit_Framework_SkippedTestError + || $e instanceof PHPUnit_Framework_AssertionFailedError) { + throw $e; + } + + // yes to stack trace, only for F tests + // PHPUnit issue 471 prevents getTrace() from being useful + throw new PHPUnit_Framework_Error($buffer, $e->getCode(), $e->getFile(), $e->getLine(), $e); + } + + private function restoreSessionStateAfterFailedTest() + { + self::$sessionId = NULL; + } + + /** + * Returns correct path to screenshot save path. + * + * @return string + */ + protected function getScreenshotPath() + { + $path = $this->screenshotPath; + + if (!in_array(substr($path, strlen($path) -1, 1), array("/","\\"))) { + $path .= DIRECTORY_SEPARATOR; + } + + return $path; + } + + /** + * Take a screenshot and return information about it. + * Return an empty string if the screenshotPath and screenshotUrl + * properties are empty. + * Issue #88. + * + * @access protected + * @return string + */ + protected function takeScreenshot() + { + if (!empty($this->screenshotPath) && + !empty($this->screenshotUrl)) { + $filename = $this->getScreenshotPath() . $this->testId . '.png'; + $kargs = ''; + if ($this->screenshotBgColor!='') { + $kargs = 'background=' . $this->screenshotBgColor; + } + + $this->drivers[0]->captureEntirePageScreenshot($filename, $kargs); + + return 'Screenshot: ' . $this->screenshotUrl . '/' . + $this->testId . ".png\n"; + } else { + return ''; + } + } + + /** + * Pause support for runSelenese() HTML cases + * @param $milliseconds + */ + protected function pause($milliseconds) + { + sleep(round($milliseconds/1000)); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.2 + */ + +/** + * TestSuite class for Selenium 1 tests + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_SeleniumTestSuite extends PHPUnit_Framework_TestSuite +{ + /** + * Overriding the default: Selenium suites are always built from a TestCase class. + * @var boolean + */ + protected $testCase = TRUE; + + /** + * Making the method public. + */ + public function addTestMethod(ReflectionClass $class, ReflectionMethod $method) + { + return parent::addTestMethod($class, $method); + } + + /** + * @param string $className extending PHPUnit_Extensions_SeleniumTestCase + * @return PHPUnit_Extensions_SeleniumTestSuite + */ + public static function fromTestCaseClass($className) + { + $suite = new self(); + $suite->setName($className); + + $class = new ReflectionClass($className); + $classGroups = PHPUnit_Util_Test::getGroups($className); + $staticProperties = $class->getStaticProperties(); + if (isset($staticProperties['browsers'])) { + $browsers = $staticProperties['browsers']; + } else if (is_callable("{$className}::browsers")) { + $browsers = $className::browsers(); + } else { + $browsers = null; + } + + //BC: renamed seleneseDirectory -> selenesePath + if (!isset($staticProperties['selenesePath']) && isset($staticProperties['seleneseDirectory'])) { + $staticProperties['selenesePath'] = $staticProperties['seleneseDirectory']; + } + + // Create tests from Selenese/HTML files. + if (isset($staticProperties['selenesePath']) && + (is_dir($staticProperties['selenesePath']) || is_file($staticProperties['selenesePath']))) { + + if (is_dir($staticProperties['selenesePath'])) { + $files = array_merge( + self::getSeleneseFiles($staticProperties['selenesePath'], '.htm'), + self::getSeleneseFiles($staticProperties['selenesePath'], '.html') + ); + } else { + $files[] = realpath($staticProperties['selenesePath']); + } + + // Create tests from Selenese/HTML files for multiple browsers. + if ($browsers) { + foreach ($browsers as $browser) { + $browserSuite = PHPUnit_Extensions_SeleniumBrowserSuite::fromClassAndBrowser($className, $browser); + + foreach ($files as $file) { + self::addGeneratedTestTo($browserSuite, + new $className($file, array(), '', $browser), + $classGroups + ); + } + + $suite->addTest($browserSuite); + } + } + else { + // Create tests from Selenese/HTML files for single browser. + foreach ($files as $file) { + self::addGeneratedTestTo($suite, + new $className($file), + $classGroups); + } + } + } + + // Create tests from test methods for multiple browsers. + if ($browsers) { + foreach ($browsers as $browser) { + $browserSuite = PHPUnit_Extensions_SeleniumBrowserSuite::fromClassAndBrowser($className, $browser); + foreach ($class->getMethods() as $method) { + $browserSuite->addTestMethod($class, $method); + } + $browserSuite->setupSpecificBrowser($browser); + + $suite->addTest($browserSuite); + } + } + else { + // Create tests from test methods for single browser. + foreach ($class->getMethods() as $method) { + $suite->addTestMethod($class, $method); + } + } + + return $suite; + } + + private static function addGeneratedTestTo(PHPUnit_Framework_TestSuite $suite, PHPUnit_Framework_TestCase $test, $classGroups) + { + list ($methodName, ) = explode(' ', $test->getName()); + $test->setDependencies( + PHPUnit_Util_Test::getDependencies(get_class($test), $methodName) + ); + $suite->addTest($test, $classGroups); + } + + /** + * @param string $directory + * @param string $suffix + * @return array + */ + private static function getSeleneseFiles($directory, $suffix) + { + $facade = new File_Iterator_Facade; + + return $facade->getFilesAsArray($directory, $suffix); + } + +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Sebastian Bergmann + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.0.0 + */ + +/** + * Implementation of the Selenium RC client/server protocol. + * + * @package PHPUnit_Selenium + * @author Sebastian Bergmann + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.0.0 + */ +class PHPUnit_Extensions_SeleniumTestCase_Driver +{ + /** + * @var PHPUnit_Extensions_SeleniumTestCase + */ + protected $testCase; + + /** + * @var string + */ + protected $testId; + + /** + * @var string + */ + protected $name; + + /** + * @var string + */ + protected $browser; + + /** + * @var string + */ + protected $browserUrl; + + /** + * @var boolean + */ + protected $collectCodeCoverageInformation = FALSE; + + /** + * @var string + */ + protected $host = 'localhost'; + + /** + * @var integer + */ + protected $port = 4444; + + /** + * @var integer + */ + protected $httpTimeout = 45; + + /** + * @var integer + */ + protected $seleniumTimeout = 30; + + /** + * @var string + */ + protected $sessionId; + + /** + * @var integer + */ + protected $sleep = 0; + + /** + * @var boolean + */ + protected $useWaitForPageToLoad = TRUE; + + /** + * @var boolean + */ + protected $wait = 5; + + /** + * @var array + */ + protected static $autoGeneratedCommands = array(); + + /** + * @var array + */ + protected $commands = array(); + + /** + * @var array $userCommands A numerical array which holds custom user commands. + */ + protected $userCommands = array(); + + /** + * @var array + */ + protected $verificationErrors = array(); + + /** + * @var array + */ + private $webDriverCapabilities; + + public function __construct() + { + if (empty(self::$autoGeneratedCommands)) { + self::autoGenerateCommands(); + } + } + + /** + * Only browserName is supported. + */ + public function setWebDriverCapabilities(array $capabilities) + { + $this->webDriverCapabilities = $capabilities; + } + + /** + * @return string + */ + public function start() + { + if ($this->browserUrl == NULL) { + throw new PHPUnit_Framework_Exception( + 'setBrowserUrl() needs to be called before start().' + ); + } + + if ($this->webDriverCapabilities !== NULL) { + $seleniumServerUrl = PHPUnit_Extensions_Selenium2TestCase_URL::fromHostAndPort($this->host, $this->port); + $driver = new PHPUnit_Extensions_Selenium2TestCase_Driver($seleniumServerUrl); + $session = $driver->startSession($this->webDriverCapabilities, new PHPUnit_Extensions_Selenium2TestCase_URL($this->browserUrl)); + $webDriverSessionId = $session->id(); + $this->sessionId = $this->getString( + 'getNewBrowserSession', + array($this->browser, $this->browserUrl, '', + "webdriver.remote.sessionid=$webDriverSessionId") + ); + + $this->doCommand('setTimeout', array($this->seleniumTimeout * 1000)); + } + + if (!isset($this->sessionId)) { + $this->sessionId = $this->getString( + 'getNewBrowserSession', + array($this->browser, $this->browserUrl) + ); + + $this->doCommand('setTimeout', array($this->seleniumTimeout * 1000)); + } + + return $this->sessionId; + } + + /** + * @return string + * @since Method available since Release 1.1.0 + */ + public function getSessionId() + { + return $this->sessionId; + } + + /** + * @param string + * @since Method available since Release 1.2.0 + */ + public function setSessionId($sessionId) + { + $this->sessionId = $sessionId; + } + + /** + */ + public function stop() + { + if (!isset($this->sessionId)) { + return; + } + + $this->doCommand('testComplete'); + + $this->sessionId = NULL; + } + + /** + * @param boolean $flag + * @throws InvalidArgumentException + */ + public function setCollectCodeCoverageInformation($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->collectCodeCoverageInformation = $flag; + } + + /** + * @param PHPUnit_Extensions_SeleniumTestCase $testCase + */ + public function setTestCase(PHPUnit_Extensions_SeleniumTestCase $testCase) + { + $this->testCase = $testCase; + } + + /** + * @param integer $testId + */ + public function setTestId($testId) + { + $this->testId = $testId; + } + + /** + * @return integer $testId + */ + public function getTestId() + { + return $this->testId; + } + + /** + * @param string $name + * @throws InvalidArgumentException + */ + public function setName($name) + { + if (!is_string($name)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->name = $name; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @param string $browser + * @throws InvalidArgumentException + */ + public function setBrowser($browser) + { + if (!is_string($browser)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->browser = $browser; + } + + /** + * @return string + */ + public function getBrowser() + { + return $this->browser; + } + + /** + * @param string $browserUrl + * @throws InvalidArgumentException + */ + public function setBrowserUrl($browserUrl) + { + if (!is_string($browserUrl)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->browserUrl = $browserUrl; + } + + /** + * @param string $host + * @throws InvalidArgumentException + */ + public function setHost($host) + { + if (!is_string($host)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->host = $host; + } + + /** + * @return string + * @since Method available since Release 1.1.0 + */ + public function getHost() + { + return $this->host; + } + + /** + * @param integer $port + * @throws InvalidArgumentException + */ + public function setPort($port) + { + if (!is_int($port)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->port = $port; + } + + /** + * @return integer + * @since Method available since Release 1.1.0 + */ + public function getPort() + { + return $this->port; + } + + /** + * @param integer $timeout for Selenium RC in seconds + * @throws InvalidArgumentException + */ + public function setTimeout($timeout) + { + if (!is_int($timeout)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->seleniumTimeout = $timeout; + } + + /** + * @param integer $timeout for HTTP connection to Selenium RC in seconds + * @throws InvalidArgumentException + */ + public function setHttpTimeout($timeout) + { + if (!is_int($timeout)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->httpTimeout = $timeout; + } + + /** + * @param integer $seconds + * @throws InvalidArgumentException + */ + public function setSleep($seconds) + { + if (!is_int($seconds)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->sleep = $seconds; + } + + /** + * Sets the number of seconds to sleep() after *AndWait commands + * when setWaitForPageToLoad(FALSE) is used. + * + * @param integer $seconds + * @throws InvalidArgumentException + */ + public function setWait($seconds) + { + if (!is_int($seconds)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer'); + } + + $this->wait = $seconds; + } + + /** + * Sets whether waitForPageToLoad (TRUE) or sleep() (FALSE) + * is used after *AndWait commands. + * + * @param boolean $flag + * @throws InvalidArgumentException + */ + public function setWaitForPageToLoad($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->useWaitForPageToLoad = $flag; + } + /** + * Sets whether captureScreenshotOnFailure (TRUE) or (FALSE) + * if true, the takeScreenshot() is triggered in onNotSuccessfulTest(). + * + * @param boolean $flag + * @throws InvalidArgumentException + */ + public function setCaptureScreenshotOnFailure($flag) + { + if (!is_bool($flag)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean'); + } + + $this->captureScreenshotOnFailure = $flag; + } + + /** + * @param string $screenshotUrl + * @throws InvalidArgumentException + */ + public function setScreenshotUrl($screenshotUrl) + { + if (!is_string($screenshotUrl)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->screenshotUrl = $screenshotUrl; + } + /** + * @param string $screenshotPath + * @throws InvalidArgumentException + */ + public function setScreenshotPath($screenshotPath) + { + if (!is_string($screenshotPath)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + + $this->screenshotPath = $screenshotPath; + } + + /** + * Adds allowed user commands into {@link self::$userCommands}. See + * {@link self::__call()} (switch/case -> default) for usage. + * + * @param string $command A command. + * + * @return $this + * @see self::__call() + */ + + + public function addUserCommand($command) + { + if (!is_string($command)) { + throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string'); + } + $this->userCommands[] = $command; + return $this; + } + + /** + * This method implements the Selenium RC protocol. + * + * @param string $command + * @param array $arguments + * @return mixed + * @method unknown addLocationStrategy() + * @method unknown addLocationStrategyAndWait() + * @method unknown addScript() + * @method unknown addScriptAndWait() + * @method unknown addSelection() + * @method unknown addSelectionAndWait() + * @method unknown allowNativeXpath() + * @method unknown allowNativeXpathAndWait() + * @method unknown altKeyDown() + * @method unknown altKeyDownAndWait() + * @method unknown altKeyUp() + * @method unknown altKeyUpAndWait() + * @method unknown answerOnNextPrompt() + * @method unknown assignId() + * @method unknown assignIdAndWait() + * @method unknown attachFile() + * @method unknown break() + * @method unknown captureEntirePageScreenshot() + * @method unknown captureEntirePageScreenshotAndWait() + * @method unknown captureEntirePageScreenshotToStringAndWait() + * @method unknown captureScreenshotAndWait() + * @method unknown captureScreenshotToStringAndWait() + * @method unknown check() + * @method unknown checkAndWait() + * @method unknown chooseCancelOnNextConfirmation() + * @method unknown chooseCancelOnNextConfirmationAndWait() + * @method unknown chooseOkOnNextConfirmation() + * @method unknown chooseOkOnNextConfirmationAndWait() + * @method unknown click() + * @method unknown clickAndWait() + * @method unknown clickAt() + * @method unknown clickAtAndWait() + * @method unknown close() + * @method unknown contextMenu() + * @method unknown contextMenuAndWait() + * @method unknown contextMenuAt() + * @method unknown contextMenuAtAndWait() + * @method unknown controlKeyDown() + * @method unknown controlKeyDownAndWait() + * @method unknown controlKeyUp() + * @method unknown controlKeyUpAndWait() + * @method unknown createCookie() + * @method unknown createCookieAndWait() + * @method unknown deleteAllVisibleCookies() + * @method unknown deleteAllVisibleCookiesAndWait() + * @method unknown deleteCookie() + * @method unknown deleteCookieAndWait() + * @method unknown deselectPopUp() + * @method unknown deselectPopUpAndWait() + * @method unknown doubleClick() + * @method unknown doubleClickAndWait() + * @method unknown doubleClickAt() + * @method unknown doubleClickAtAndWait() + * @method unknown dragAndDrop() + * @method unknown dragAndDropAndWait() + * @method unknown dragAndDropToObject() + * @method unknown dragAndDropToObjectAndWait() + * @method unknown dragDrop() + * @method unknown dragDropAndWait() + * @method unknown echo() + * @method unknown fireEvent() + * @method unknown fireEventAndWait() + * @method unknown focus() + * @method unknown focusAndWait() + * @method string getAlert() + * @method array getAllButtons() + * @method array getAllFields() + * @method array getAllLinks() + * @method array getAllWindowIds() + * @method array getAllWindowNames() + * @method array getAllWindowTitles() + * @method string getAttribute(string $attributeLocator) + * @method array getAttributeFromAllWindows(string $attributeName) + * @method string getBodyText() + * @method string getConfirmation() + * @method string getCookie() + * @method string getCookieByName(string $name) + * @method integer getCssCount(string $locator) + * @method integer getCursorPosition(string $locator) + * @method integer getElementHeight(string $locator) + * @method integer getElementIndex(string $locator) + * @method integer getElementPositionLeft(string $locator) + * @method integer getElementPositionTop(string $locator) + * @method integer getElementWidth(string $locator) + * @method string getEval(string $script) + * @method string getExpression(string $expression) + * @method string getHtmlSource() + * @method string getLocation() + * @method string getLogMessages() + * @method integer getMouseSpeed() + * @method string getPrompt() + * @method array getSelectOptions(string $selectLocator) + * @method string getSelectedId(string $selectLocator) + * @method array getSelectedIds(string $selectLocator) + * @method string getSelectedIndex(string $selectLocator) + * @method array getSelectedIndexes(string $selectLocator) + * @method string getSelectedLabel(string $selectLocator) + * @method array getSelectedLabels(string $selectLocator) + * @method string getSelectedValue(string $selectLocator) + * @method array getSelectedValues(string $selectLocator) + * @method unknown getSpeed() + * @method unknown getSpeedAndWait() + * @method string getTable(string $tableCellAddress) + * @method string getText(string $locator) + * @method string getTitle() + * @method string getValue(string $locator) + * @method boolean getWhetherThisFrameMatchFrameExpression(string $currentFrameString, string $target) + * @method boolean getWhetherThisWindowMatchWindowExpression(string $currentWindowString, string $target) + * @method integer getXpathCount(string $xpath) + * @method unknown goBack() + * @method unknown goBackAndWait() + * @method unknown highlight(string $locator) + * @method unknown highlightAndWait(string $locator) + * @method unknown ignoreAttributesWithoutValue(string $ignore) + * @method unknown ignoreAttributesWithoutValueAndWait(string $ignore) + * @method boolean isAlertPresent() + * @method boolean isChecked(locator) + * @method boolean isConfirmationPresent() + * @method boolean isCookiePresent(string $name) + * @method boolean isEditable(string $locator) + * @method boolean isElementPresent(string $locator) + * @method boolean isOrdered(string $locator1, string $locator2) + * @method boolean isPromptPresent() + * @method boolean isSomethingSelected(string $selectLocator) + * @method boolean isTextPresent(pattern) + * @method boolean isVisible(locator) + * @method unknown keyDown() + * @method unknown keyDownAndWait() + * @method unknown keyDownNative() + * @method unknown keyDownNativeAndWait() + * @method unknown keyPress() + * @method unknown keyPressAndWait() + * @method unknown keyPressNative() + * @method unknown keyPressNativeAndWait() + * @method unknown keyUp() + * @method unknown keyUpAndWait() + * @method unknown keyUpNative() + * @method unknown keyUpNativeAndWait() + * @method unknown metaKeyDown() + * @method unknown metaKeyDownAndWait() + * @method unknown metaKeyUp() + * @method unknown metaKeyUpAndWait() + * @method unknown mouseDown() + * @method unknown mouseDownAndWait() + * @method unknown mouseDownAt() + * @method unknown mouseDownAtAndWait() + * @method unknown mouseMove() + * @method unknown mouseMoveAndWait() + * @method unknown mouseMoveAt() + * @method unknown mouseMoveAtAndWait() + * @method unknown mouseOut() + * @method unknown mouseOutAndWait() + * @method unknown mouseOver() + * @method unknown mouseOverAndWait() + * @method unknown mouseUp() + * @method unknown mouseUpAndWait() + * @method unknown mouseUpAt() + * @method unknown mouseUpAtAndWait() + * @method unknown mouseUpRight() + * @method unknown mouseUpRightAndWait() + * @method unknown mouseUpRightAt() + * @method unknown mouseUpRightAtAndWait() + * @method unknown open() + * @method unknown openWindow() + * @method unknown openWindowAndWait() + * @method unknown pause() + * @method unknown refresh() + * @method unknown refreshAndWait() + * @method unknown removeAllSelections() + * @method unknown removeAllSelectionsAndWait() + * @method unknown removeScript() + * @method unknown removeScriptAndWait() + * @method unknown removeSelection() + * @method unknown removeSelectionAndWait() + * @method unknown retrieveLastRemoteControlLogs() + * @method unknown rollup() + * @method unknown rollupAndWait() + * @method unknown runScript() + * @method unknown runScriptAndWait() + * @method unknown select() + * @method unknown selectAndWait() + * @method unknown selectFrame() + * @method unknown selectPopUp() + * @method unknown selectPopUpAndWait() + * @method unknown selectWindow() + * @method unknown setBrowserLogLevel() + * @method unknown setBrowserLogLevelAndWait() + * @method unknown setContext() + * @method unknown setCursorPosition() + * @method unknown setCursorPositionAndWait() + * @method unknown setMouseSpeed() + * @method unknown setMouseSpeedAndWait() + * @method unknown setSpeed() + * @method unknown setSpeedAndWait() + * @method unknown shiftKeyDown() + * @method unknown shiftKeyDownAndWait() + * @method unknown shiftKeyUp() + * @method unknown shiftKeyUpAndWait() + * @method unknown shutDownSeleniumServer() + * @method unknown store() + * @method unknown submit() + * @method unknown submitAndWait() + * @method unknown type() + * @method unknown typeAndWait() + * @method unknown typeKeys() + * @method unknown typeKeysAndWait() + * @method unknown uncheck() + * @method unknown uncheckAndWait() + * @method unknown useXpathLibrary() + * @method unknown useXpathLibraryAndWait() + * @method unknown waitForCondition() + * @method unknown waitForElementPresent() + * @method unknown waitForElementNotPresent() + * @method unknown waitForPageToLoad() + * @method unknown waitForPopUp() + * @method unknown windowFocus() + * @method unknown windowMaximize() + */ + public function __call($command, $arguments) + { + $arguments = $this->preprocessParameters($arguments); + + $wait = FALSE; + + if (substr($command, -7, 7) == 'AndWait') { + $command = substr($command, 0, -7); + $wait = TRUE; + } + + switch ($command) { + case 'addLocationStrategy': + case 'addScript': + case 'addSelection': + case 'allowNativeXpath': + case 'altKeyDown': + case 'altKeyUp': + case 'answerOnNextPrompt': + case 'assignId': + case 'attachFile': + case 'break': + case 'captureEntirePageScreenshot': + case 'captureScreenshot': + case 'check': + case 'chooseCancelOnNextConfirmation': + case 'chooseOkOnNextConfirmation': + case 'click': + case 'clickAt': + case 'close': + case 'contextMenu': + case 'contextMenuAt': + case 'controlKeyDown': + case 'controlKeyUp': + case 'createCookie': + case 'deleteAllVisibleCookies': + case 'deleteCookie': + case 'deselectPopUp': + case 'doubleClick': + case 'doubleClickAt': + case 'dragAndDrop': + case 'dragAndDropToObject': + case 'dragDrop': + case 'echo': + case 'fireEvent': + case 'focus': + case 'goBack': + case 'highlight': + case 'ignoreAttributesWithoutValue': + case 'keyDown': + case 'keyDownNative': + case 'keyPress': + case 'keyPressNative': + case 'keyUp': + case 'keyUpNative': + case 'metaKeyDown': + case 'metaKeyUp': + case 'mouseDown': + case 'mouseDownAt': + case 'mouseMove': + case 'mouseMoveAt': + case 'mouseOut': + case 'mouseOver': + case 'mouseUp': + case 'mouseUpAt': + case 'mouseUpRight': + case 'mouseUpRightAt': + case 'open': + case 'openWindow': + case 'pause': + case 'refresh': + case 'removeAllSelections': + case 'removeScript': + case 'removeSelection': + case 'retrieveLastRemoteControlLogs': + case 'rollup': + case 'runScript': + case 'select': + case 'selectFrame': + case 'selectPopUp': + case 'selectWindow': + case 'setBrowserLogLevel': + case 'setContext': + case 'setCursorPosition': + case 'setMouseSpeed': + case 'setSpeed': + case 'shiftKeyDown': + case 'shiftKeyUp': + case 'shutDownSeleniumServer': + case 'store': + case 'submit': + case 'type': + case 'typeKeys': + case 'uncheck': + case 'useXpathLibrary': + case 'windowFocus': + case 'windowMaximize': + case isset(self::$autoGeneratedCommands[$command]): { + // Pre-Command Actions + switch ($command) { + case 'open': + case 'openWindow': { + if ($this->collectCodeCoverageInformation) { + $this->deleteCookie('PHPUNIT_SELENIUM_TEST_ID', 'path=/'); + + $this->createCookie( + 'PHPUNIT_SELENIUM_TEST_ID=' . $this->testId, + 'path=/' + ); + } + } + break; + case 'store': + // store is a synonym of storeExpression + // and RC only understands storeExpression + $command = 'storeExpression'; + break; + } + + if (isset(self::$autoGeneratedCommands[$command]) && self::$autoGeneratedCommands[$command]['functionHelper']) { + $helperArguments = array($command, $arguments, self::$autoGeneratedCommands[$command]); + call_user_func_array(array($this, self::$autoGeneratedCommands[$command]['functionHelper']), $helperArguments); + } else { + $this->doCommand($command, $arguments); + } + + // Post-Command Actions + switch ($command) { + case 'addLocationStrategy': + case 'allowNativeXpath': + case 'assignId': + case 'captureEntirePageScreenshot': + case 'captureScreenshot': { + // intentionally empty + } + break; + + default: { + if ($wait) { + if ($this->useWaitForPageToLoad) { + $this->waitForPageToLoad($this->seleniumTimeout * 1000); + } else { + sleep($this->wait); + } + } + + if ($this->sleep > 0) { + sleep($this->sleep); + } + + $this->testCase->runDefaultAssertions($command); + } + } + } + break; + + case 'getWhetherThisFrameMatchFrameExpression': + case 'getWhetherThisWindowMatchWindowExpression': + case 'isAlertPresent': + case 'isChecked': + case 'isConfirmationPresent': + case 'isCookiePresent': + case 'isEditable': + case 'isElementPresent': + case 'isOrdered': + case 'isPromptPresent': + case 'isSomethingSelected': + case 'isTextPresent': + case 'isVisible': { + return $this->getBoolean($command, $arguments); + } + break; + + case 'getCssCount': + case 'getCursorPosition': + case 'getElementHeight': + case 'getElementIndex': + case 'getElementPositionLeft': + case 'getElementPositionTop': + case 'getElementWidth': + case 'getMouseSpeed': + case 'getSpeed': + case 'getXpathCount': { + $result = $this->getNumber($command, $arguments); + + if ($wait) { + $this->waitForPageToLoad($this->seleniumTimeout * 1000); + } + + return $result; + } + break; + + case 'getAlert': + case 'getAttribute': + case 'getBodyText': + case 'getConfirmation': + case 'getCookie': + case 'getCookieByName': + case 'getEval': + case 'getExpression': + case 'getHtmlSource': + case 'getLocation': + case 'getLogMessages': + case 'getPrompt': + case 'getSelectedId': + case 'getSelectedIndex': + case 'getSelectedLabel': + case 'getSelectedValue': + case 'getTable': + case 'getText': + case 'getTitle': + case 'captureEntirePageScreenshotToString': + case 'captureScreenshotToString': + case 'getValue': { + $result = $this->getString($command, $arguments); + + if ($wait) { + $this->waitForPageToLoad($this->seleniumTimeout * 1000); + } + + return $result; + } + break; + + case 'getAllButtons': + case 'getAllFields': + case 'getAllLinks': + case 'getAllWindowIds': + case 'getAllWindowNames': + case 'getAllWindowTitles': + case 'getAttributeFromAllWindows': + case 'getSelectedIds': + case 'getSelectedIndexes': + case 'getSelectedLabels': + case 'getSelectedValues': + case 'getSelectOptions': { + $result = $this->getStringArray($command, $arguments); + + if ($wait) { + $this->waitForPageToLoad($this->seleniumTimeout * 1000); + } + + return $result; + } + break; + + case 'waitForCondition': + case 'waitForElementPresent': + case 'waitForElementNotPresent': + case 'waitForFrameToLoad': + case 'waitForPopUp': { + if (count($arguments) == 1) { + $arguments[] = $this->seleniumTimeout * 1000; + } + + $this->doCommand($command, $arguments); + $this->testCase->runDefaultAssertions($command); + } + break; + + case 'waitForPageToLoad': { + if (empty($arguments)) { + $arguments[] = $this->seleniumTimeout * 1000; + } + + $this->doCommand($command, $arguments); + $this->testCase->runDefaultAssertions($command); + } + break; + + default: { + if (!in_array($command, $this->userCommands)) { + throw new BadMethodCallException( + "Method $command not defined." + ); + } + $this->doCommand($command, $arguments); + } + } + } + + /** + * Send a command to the Selenium RC server. + * + * @param string $command + * @param array $arguments + * @param array $namedArguments + * @return string + * @author Seth Casana + */ + protected function doCommand($command, array $arguments = array(), array $namedArguments = array()) + { + $url = sprintf( + 'http://%s:%s/selenium-server/driver/', + $this->host, + $this->port + ); + + $numArguments = count($arguments); + $postData = sprintf('cmd=%s', urlencode($command)); + for ($i = 0; $i < $numArguments; $i++) { + $argNum = strval($i + 1); + + if ($arguments[$i] == ' ') { + $postData .= sprintf('&%s=%s', $argNum, urlencode($arguments[$i])); + } else { + $postData .= sprintf('&%s=%s', $argNum, urlencode(trim($arguments[$i]))); + } + } + foreach ($namedArguments as $key => $value) { + $postData .= sprintf('&%s=%s', $key, urlencode($value)); + } + + if (isset($this->sessionId)) { + $postData .= sprintf('&%s=%s', 'sessionId', $this->sessionId); + } + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, $url); + curl_setopt($curl, CURLOPT_HEADER, 0); + curl_setopt($curl, CURLOPT_POST, TRUE); + curl_setopt($curl, CURLOPT_POSTFIELDS, $postData); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Content-Type: application/x-www-form-urlencoded; charset=utf-8' + )); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 60); + + $response = curl_exec($curl); + $info = curl_getinfo($curl); + + if (!$response) { + throw new RuntimeException("CURL error while accessing the Selenium Server at '$url': " . curl_error($curl)); + } + + curl_close($curl); + + if (!preg_match('/^OK/', $response)) { + throw new RuntimeException("Invalid response while accessing the Selenium Server at '$url': " . $response); + } + + if ($info['http_code'] != 200) { + throw new RuntimeException( + 'The response from the Selenium RC server is invalid: ' . + $response + ); + } + + return $response; + } + + protected function preprocessParameters($params) + { + foreach ($params as $key => $param ) { + if (is_string($param) && (strlen($param) > 0)) { + $params[$key] = $this->getString('getExpression', array($param)); + } + } + return $params; + } + + /** + * Send a command to the Selenium RC server and treat the result + * as a boolean. + * + * @param string $command + * @param array $arguments + * @return boolean + * @author Shin Ohno + * @author Bjoern Schotte + */ + protected function getBoolean($command, array $arguments) + { + $result = $this->getString($command, $arguments); + + switch ($result) { + case 'true': return TRUE; + + case 'false': return FALSE; + + default: { + throw new PHPUnit_Framework_Exception( + 'Result is neither "true" nor "false": ' . PHPUnit_Util_Type::export($result) + ); + } + } + } + + /** + * Send a command to the Selenium RC server and treat the result + * as a number. + * + * @param string $command + * @param array $arguments + * @return numeric + * @author Shin Ohno + * @author Bjoern Schotte + */ + protected function getNumber($command, array $arguments) + { + $result = $this->getString($command, $arguments); + + if (!is_numeric($result)) { + throw new PHPUnit_Framework_Exception( + 'Result is not numeric: ' . PHPUnit_Util_Type::export($result) + ); + } + + return $result; + } + + /** + * Send a command to the Selenium RC server and treat the result + * as a string. + * + * @param string $command + * @param array $arguments + * @return string + * @author Shin Ohno + * @author Bjoern Schotte + */ + protected function getString($command, array $arguments) + { + try { + $result = $this->doCommand($command, $arguments); + } + + catch (RuntimeException $e) { + throw $e; + } + + return (strlen($result) > 3) ? substr($result, 3) : ''; + } + + /** + * Send a command to the Selenium RC server and treat the result + * as an array of strings. + * + * @param string $command + * @param array $arguments + * @return array + * @author Shin Ohno + * @author Bjoern Schotte + */ + protected function getStringArray($command, array $arguments) + { + $csv = $this->getString($command, $arguments); + $token = ''; + $tokens = array(); + $letters = preg_split('//', $csv, -1, PREG_SPLIT_NO_EMPTY); + $count = count($letters); + + for ($i = 0; $i < $count; $i++) { + $letter = $letters[$i]; + + switch($letter) { + case '\\': { + $letter = $letters[++$i]; + $token .= $letter; + } + break; + + case ',': { + $tokens[] = $token; + $token = ''; + } + break; + + default: { + $token .= $letter; + } + } + } + + $tokens[] = $token; + + return $tokens; + } + + public function getVerificationErrors() + { + return $this->verificationErrors; + } + + public function clearVerificationErrors() + { + $this->verificationErrors = array(); + } + + protected function assertCommand($command, $arguments, $info) + { + $method = $info['originalMethod']; + $requiresTarget = $info['requiresTarget']; + $result = $this->__call($method, $arguments); + $message = "Failed command: " . $command . "('" + . (array_key_exists(0, $arguments) ? $arguments[0] . "'" : '') + . (array_key_exists(1, $arguments) ? ", '" . $arguments[1] . "'" : '') + . ")"; + + if ($info['isBoolean']) { + if (!isset($info['negative']) || !$info['negative']) { + PHPUnit_Framework_Assert::assertTrue($result, $message); + } else { + PHPUnit_Framework_Assert::assertFalse($result, $message); + } + } else { + if ($requiresTarget === TRUE) { + $expected = $arguments[1]; + } else { + $expected = $arguments[0]; + } + + if (strpos($expected, 'exact:') === 0) { + $expected = substr($expected, strlen('exact:')); + + if (!isset($info['negative']) || !$info['negative']) { + PHPUnit_Framework_Assert::assertEquals($expected, $result, $message); + } else { + PHPUnit_Framework_Assert::assertNotEquals($expected, $result, $message); + } + } else { + $caseInsensitive = FALSE; + + if (strpos($expected, 'regexp:') === 0) { + $expected = substr($expected, strlen('regexp:')); + } + + else if (strpos($expected, 'regexpi:') === 0) { + $expected = substr($expected, strlen('regexpi:')); + $caseInsensitive = TRUE; + } + + else { + if (strpos($expected, 'glob:') === 0) { + $expected = substr($expected, strlen('glob:')); + } + + $expected = '^' . str_replace( + array('*', '?'), array('.*', '.?'), $expected + ) . '$'; + } + + $expected = '/' . str_replace('/', '\/', $expected) . '/'; + + if ($caseInsensitive) { + $expected .= 'i'; + } + + if (!isset($info['negative']) || !$info['negative']) { + PHPUnit_Framework_Assert::assertRegExp( + $expected, $result, $message + ); + } else { + PHPUnit_Framework_Assert::assertNotRegExp( + $expected, $result, $message + ); + } + } + } + } + + protected function verifyCommand($command, $arguments, $info) + { + try { + $this->assertCommand($command, $arguments, $info); + } + + catch (PHPUnit_Framework_AssertionFailedError $e) { + array_push($this->verificationErrors, $e->toString()); + } + } + + protected function waitForCommand($command, $arguments, $info) + { + $lastExceptionMessage = ''; + for ($second = 0; ; $second++) { + if ($second > $this->httpTimeout) { + PHPUnit_Framework_Assert::fail( + "WaitFor timeout. \n" + . "Last exception message: \n" . $lastExceptionMessage + ); + } + + try { + $this->assertCommand($command, $arguments, $info); + return; + } + + catch (Exception $e) { + $lastExceptionMessage = $e->getMessage(); + } + + sleep(1); + } + } + + /** + * Parses the docblock of PHPUnit_Extensions_SeleniumTestCase_Driver::__call + * for get*(), is*(), assert*(), verify*(), assertNot*(), verifyNot*(), + * store*(), waitFor*(), and waitForNot*() methods. + */ + protected static function autoGenerateCommands() + { + $method = new ReflectionMethod(__CLASS__, '__call'); + $docComment = $method->getDocComment(); + + if (preg_match_all('(@method\s+(\w+)\s+([\w]+)\((.*)\))', $docComment, $matches)) { + foreach ($matches[2] as $methodKey => $method) { + if (preg_match('/^(get|is)([A-Z].+)$/', $method, $methodMatches)) { + $baseName = $methodMatches[2]; + $isBoolean = $methodMatches[1] == 'is'; + $requiresTarget = (strlen($matches[3][$methodKey]) > 0); + + if (preg_match('/^(.*)Present$/', $baseName, $methodMatches)) { + $notBaseName = $methodMatches[1] . 'NotPresent'; + } else { + $notBaseName = 'Not' . $baseName; + } + + self::$autoGeneratedCommands['store' . $baseName] = array( + 'functionHelper' => FALSE + ); + + self::$autoGeneratedCommands['assert' . $baseName] = array( + 'originalMethod' => $method, + 'isBoolean' => $isBoolean, + 'functionHelper' => 'assertCommand', + 'requiresTarget' => $requiresTarget + ); + + self::$autoGeneratedCommands['assert' . $notBaseName] = array( + 'originalMethod' => $method, + 'isBoolean' => $isBoolean, + 'negative' => TRUE, + 'functionHelper' => 'assertCommand', + 'requiresTarget' => $requiresTarget + ); + + self::$autoGeneratedCommands['verify' . $baseName] = array( + 'originalMethod' => $method, + 'isBoolean' => $isBoolean, + 'functionHelper' => 'verifyCommand', + 'requiresTarget' => $requiresTarget + ); + + self::$autoGeneratedCommands['verify' . $notBaseName] = array( + 'originalMethod' => $method, + 'isBoolean' => $isBoolean, + 'negative' => TRUE, + 'functionHelper' => 'verifyCommand', + 'requiresTarget' => $requiresTarget + ); + + self::$autoGeneratedCommands['waitFor' . $baseName] = array( + 'originalMethod' => $method, + 'isBoolean' => $isBoolean, + 'functionHelper' => 'waitForCommand', + 'requiresTarget' => $requiresTarget + ); + + self::$autoGeneratedCommands['waitFor' . $notBaseName] = array( + 'originalMethod' => $method, + 'isBoolean' => $isBoolean, + 'negative' => TRUE, + 'functionHelper' => 'waitForCommand', + 'requiresTarget' => $requiresTarget + ); + } + } + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.12 + */ + +/** + * Class to hold the special keys Unicode entities + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.3.0 + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value + */ +class PHPUnit_Extensions_Selenium2TestCase_Keys +{ + const NULL = "\xEE\x80\x80"; + const CANCEL = "\xEE\x80\x81"; + const HELP = "\xEE\x80\x82"; + const BACKSPACE = "\xEE\x80\x83"; + const TAB = "\xEE\x80\x84"; + const CLEAR = "\xEE\x80\x85"; + const RETURN_ = "\xEE\x80\x86"; + const ENTER = "\xEE\x80\x87"; + const SHIFT = "\xEE\x80\x88"; + const CONTROL = "\xEE\x80\x89"; + const ALT = "\xEE\x80\x8A"; + const PAUSE = "\xEE\x80\x8B"; + const ESCAPE = "\xEE\x80\x8C"; + const SPACE = "\xEE\x80\x8D"; + const PAGEUP = "\xEE\x80\x8E"; + const PAGEDOWN = "\xEE\x80\x8F"; + const END = "\xEE\x80\x90"; + const HOME = "\xEE\x80\x91"; + const LEFT = "\xEE\x80\x92"; + const UP = "\xEE\x80\x93"; + const RIGHT = "\xEE\x80\x94"; + const DOWN = "\xEE\x80\x95"; + const INSERT = "\xEE\x80\x96"; + const DELETE = "\xEE\x80\x97"; + const SEMICOLON = "\xEE\x80\x98"; + const EQUALS = "\xEE\x80\x99"; + const NUMPAD0 = "\xEE\x80\x9A"; + const NUMPAD1 = "\xEE\x80\x9B"; + const NUMPAD2 = "\xEE\x80\x9C"; + const NUMPAD3 = "\xEE\x80\x9D"; + const NUMPAD4 = "\xEE\x80\x9E"; + const NUMPAD5 = "\xEE\x80\x9F"; + const NUMPAD6 = "\xEE\x80\xA0"; + const NUMPAD7 = "\xEE\x80\xA1"; + const NUMPAD8 = "\xEE\x80\xA2"; + const NUMPAD9 = "\xEE\x80\xA3"; + const MULTIPLY = "\xEE\x80\xA4"; + const ADD = "\xEE\x80\xA5"; + const SEPARATOR = "\xEE\x80\xA6"; + const SUBTRACT = "\xEE\x80\xA7"; + const DECIMAL = "\xEE\x80\xA8"; + const DIVIDE = "\xEE\x80\xA9"; + const F1 = "\xEE\x80\xB1"; + const F2 = "\xEE\x80\xB2"; + const F3 = "\xEE\x80\xB3"; + const F4 = "\xEE\x80\xB4"; + const F5 = "\xEE\x80\xB5"; + const F6 = "\xEE\x80\xB6"; + const F7 = "\xEE\x80\xB7"; + const F8 = "\xEE\x80\xB8"; + const F9 = "\xEE\x80\xB9"; + const F10 = "\xEE\x80\xBA"; + const F11 = "\xEE\x80\xBB"; + const F12 = "\xEE\x80\xBC"; + const COMMAND = "\xEE\x80\xBD"; +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Adds a cookie. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder +{ + private $name; + private $value; + private $path; + private $domain; + private $secure = FALSE; + private $expiry; + + public function __construct($cookieFacade, $name, $value) + { + $this->cookieFacade = $cookieFacade; + $this->name = $name; + $this->value = $value; + } + + /** + * @param string + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder + */ + public function path($path) + { + $this->path = $path; + return $this; + } + + /** + * @param string + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder + */ + public function domain($domain) + { + $this->domain = $domain; + return $this; + } + + /** + * @param boolean + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder + */ + public function secure($secure) + { + $this->secure = $secure; + return $this; + } + + /** + * @param integer + * @return PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder + */ + public function expiry($expiry) + { + $this->expiry = $expiry; + return $this; + } + + /** + * @return void + */ + public function set() + { + $cookieData = array( + 'name' => $this->name, + 'value' => $this->value, + 'secure' => $this->secure, + ); + foreach (array('path', 'domain', 'expiry') as $parameter) { + if ($this->$parameter !== NULL) { + $cookieData[$parameter] = $this->$parameter; + } + } + $this->cookieFacade->postCookie($cookieData); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Manages timeouts for the current browser session. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + * @method implicitWait(int $ms) Sets timeout when searching for elements + * @method asyncScript(int $ms) Sets timeout for asynchronous scripts executed by Session::executeAsync() + */ +class PHPUnit_Extensions_Selenium2TestCase_Session_Timeouts + extends PHPUnit_Extensions_Selenium2TestCase_CommandsHolder +{ + private $maximumTimeout; + private $lastImplicitWaitValue = 0; + + public function __construct($driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url, + $maximumTimeout) + { + parent::__construct($driver, $url); + $this->maximumTimeout = $maximumTimeout; + } + + protected function initCommands() + { + $self = $this; + return array( + 'implicitWait' => function ($milliseconds, $commandUrl) use ($self) { + $self->check($milliseconds); + $self->setLastImplicitWaitValue($milliseconds); + $jsonParameters = array('ms' => $milliseconds); + return new PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost($jsonParameters, $commandUrl); + }, + 'asyncScript' => function ($milliseconds, $commandUrl) use ($self) { + $self->check($milliseconds); + $jsonParameters = array('ms' => $milliseconds); + return new PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost($jsonParameters, $commandUrl); + }, + + ); + } + + public function setLastImplicitWaitValue($implicitWait) + { + $this->lastImplicitWaitValue = $implicitWait; + } + + public function getLastImplicitWaitValue() + { + return $this->lastImplicitWaitValue; + } + + public function check($timeout) + { + if ($timeout > $this->maximumTimeout) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception('There is no use in setting this timeout unless you also call $this->setSeleniumServerRequestsTimeout($seconds) in setUp().'); + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Manage the local storage HTML 5 database. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_Session_Storage +{ + private $driver; + private $url; + + public function __construct(PHPUnit_Extensions_Selenium2TestCase_Driver $driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + $this->driver = $driver; + $this->url = $url; + } + + public function __set($name, $value) + { + $this->driver->curl('POST', $this->url, array( + 'key' => $name, + 'value' => (string)$value + )); + } + + public function __get($name) + { + return $this->driver->curl( + 'GET', + $this->url->descend('key')->descend($name) + )->getValue(); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Adds and remove cookies. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_Session_Cookie +{ + private $driver; + private $url; + + public function __construct(PHPUnit_Extensions_Selenium2TestCase_Driver $driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + $this->driver = $driver; + $this->url = $url; + } + + /** + * @param string $name + * @param string $value + * @return void + */ + public function add($name, $value) + { + return new PHPUnit_Extensions_Selenium2TestCase_Session_Cookie_Builder($this, $name, $value); + } + + /** + * @param string $name + * @return string + */ + public function get($name) + { + $cookies = $this->driver->curl('GET', $this->url)->getValue(); + foreach ($cookies as $cookie) { + if ($cookie['name'] == $name) { + return $cookie['value']; + } + } + throw new PHPUnit_Extensions_Selenium2TestCase_Exception("There is no '$name' cookie available on this page."); + } + + /** + * @param string $name + * @return void + */ + public function remove($name) + { + $url = $this->url->descend($name); + $this->driver->curl('DELETE', $url); + } + + /** + * @return void + */ + public function clear() + { + $this->driver->curl('DELETE', $this->url); + } + + /** + * @internal + * @param array $data + * @return void + */ + public function postCookie(array $data) + { + $this->driver->curl('POST', + $this->url, + array( + 'cookie' => $data + )); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Indicates an exception during the execution of Selenium 2 commands. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_Exception extends RuntimeException +{ +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Object representing elements, or everything that may have subcommands. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +abstract class PHPUnit_Extensions_Selenium2TestCase_CommandsHolder +{ + /** + * @var PHPUnit_Extensions_Selenium2TestCase_Driver + */ + protected $driver; + + /** + * @var string the API URL for this element, + */ + protected $url; + + /** + * @var array instances of + * PHPUnit_Extensions_Selenium2TestCase_ElementCommand + */ + protected $commands; + + public function __construct($driver, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + $this->driver = $driver; + $this->url = $url; + $this->commands = array(); + foreach ($this->initCommands() as $commandName => $handler) { + if (is_string($handler)) { + $this->commands[$commandName] = $this->factoryMethod($handler); + } else if (is_callable($handler)) { + $this->commands[$commandName] = $handler; + } else { + throw new InvalidArgumentException("Command $commandName is not configured correctly."); + } + } + } + + /** + * @return array class names, or + * callables of the form function($parameter, $commandUrl) + */ + protected abstract function initCommands(); + + public function __call($commandName, $arguments) + { + $jsonParameters = $this->extractJsonParameters($arguments); + $response = $this->driver->execute($this->newCommand($commandName, $jsonParameters)); + return $response->getValue(); + } + + protected function postCommand($name, PHPUnit_Extensions_Selenium2TestCase_ElementCriteria $criteria) + { + $response = $this->driver->curl('POST', + $this->url->addCommand($name), + $criteria->getArrayCopy()); + return $response->getValue(); + } + + /** + * @params string $commandClass a class name, descending from + PHPUnit_Extensions_Selenium2TestCase_Command + * @return callable + */ + private function factoryMethod($commandClass) + { + return function($jsonParameters, $url) use ($commandClass) { + return new $commandClass($jsonParameters, $url); + }; + } + + private function extractJsonParameters($arguments) + { + $this->checkArguments($arguments); + + if (count($arguments) == 0) { + return NULL; + } + return $arguments[0]; + } + + private function checkArguments($arguments) + { + if (count($arguments) > 1) { + throw new Exception('You cannot call a command with multiple method arguments.'); + } + } + + /** + * @param string $commandName The called method name + * defined as a key in initCommands() + * @param array $jsonParameters + * @return PHPUnit_Extensions_Selenium2TestCase_Command + */ + protected function newCommand($commandName, $jsonParameters) + { + if (isset($this->commands[$commandName])) { + $factoryMethod = $this->commands[$commandName]; + $url = $this->url->addCommand($commandName); + $command = $factoryMethod($jsonParameters, $url); + return $command; + } + throw new BadMethodCallException("The command '$commandName' is not existent or not supported yet."); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.8 + */ + +/** + * Base class for implementing commands with special semantics. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.8 + */ +class PHPUnit_Extensions_Selenium2TestCase_ScreenshotListener implements PHPUnit_Framework_TestListener +{ + private $directory; + + public function __construct($directory) + { + $this->directory = $directory; + } + + public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) + { + $this->storeAScreenshot($test); + } + + public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + { + $this->storeAScreenshot($test); + } + + private function storeAScreenshot(PHPUnit_Framework_Test $test) + { + if ($test instanceof PHPUnit_Extensions_Selenium2TestCase) + { + try { + $file = $this->directory . '/' . get_class($test) . '__' . $test->getName() . '__' . date('Y-m-d\TH-i-s') . '.png'; + file_put_contents($file, $test->currentScreenshot()); + } catch (Exception $e) { + $file = $this->directory . '/' . get_class($test) . '__' . $test->getName() . '__' . date('Y-m-d\TH-i-s') . '.txt'; + file_put_contents($file, "Screenshot generation doesn't work." . "\n" + . $e->getMessage() . "\n" + . $e->getTraceAsString()); + } + } + } + + public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {} + public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time) {} + public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) {} + public function startTest(PHPUnit_Framework_Test $test) {} + public function endTest(PHPUnit_Framework_Test $test, $time) {} + public function startTestSuite(PHPUnit_Framework_TestSuite $suite) {} + public function endTestSuite(PHPUnit_Framework_TestSuite $suite) {} +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Base class for implementing commands with special semantics. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +abstract class PHPUnit_Extensions_Selenium2TestCase_Command +{ + protected $jsonParameters; + private $commandName; + + /** + * @param array $jsonParameters null in case of no parameters + */ + public function __construct($jsonParameters, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (!is_array($jsonParameters) && $jsonParameters !== NULL) { + throw new InvalidArgumentException("The JSON parameters must be an array, or a NULL value in case they are not required."); + } + $this->jsonParameters = $jsonParameters; + $this->url = $url; + } + + public function url() + { + return $this->url; + } + + /** + * @return string + */ + abstract public function httpMethod(); + + /** + * @param array $jsonParameters null in case of no parameters + */ + public function jsonParameters() + { + return $this->jsonParameters; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.5 + */ + +/** + * Gets or sets an attribute of an object. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.5 + */ +class PHPUnit_Extensions_Selenium2TestCase_StateCommand + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Object representing a DOM element. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + * @method string attribute($name) Retrieves an element's attribute + * @method void clear() Empties the content of a form element. + * @method void click() Clicks on element + * @method string css($propertyName) Retrieves the value of a CSS property + * @method bool displayed() Checks an element's visibility + * @method bool enabled() Checks a form element's state + * @method bool equals(PHPUnit_Extensions_Selenium2TestCase_Element $another) Checks if the two elements are the same on the page + * @method array location() Retrieves the element's position in the page: keys 'x' and 'y' in the returned array + * @method bool selected() Checks the state of an option or other form element + * @method array size() Retrieves the dimensions of the element: 'width' and 'height' of the returned array + * @method void submit() Submits a form; can be called on its children + * @method string text() Get content of ordinary elements + */ +class PHPUnit_Extensions_Selenium2TestCase_Element + extends PHPUnit_Extensions_Selenium2TestCase_Element_Accessor +{ + /** + * @return \self + * @throws InvalidArgumentException + */ + public static function fromResponseValue( + array $value, + PHPUnit_Extensions_Selenium2TestCase_URL $parentFolder, + PHPUnit_Extensions_Selenium2TestCase_Driver $driver) + { + if (!isset($value['ELEMENT'])) { + throw new InvalidArgumentException('Element not found.'); + } + $url = $parentFolder->descend($value['ELEMENT']); + return new self($driver, $url); + } + + /** + * @return integer + */ + public function getId() + { + return $this->url->lastSegment(); + } + + /** + * @return array class names + */ + protected function initCommands() + { + return array( + 'attribute' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Attribute', + 'clear' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'click' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Click', + 'css' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Css', + 'displayed' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'enabled' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'equals' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Equals', + 'location' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'name' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'selected' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'size' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'submit' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + 'text' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor', + 'value' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Value', + 'tap' => $this->touchCommandFactoryMethod('touch/click'), + 'scroll' => $this->touchCommandFactoryMethod('touch/scroll'), + 'doubletap' => $this->touchCommandFactoryMethod('touch/doubleclick'), + 'longtap' => $this->touchCommandFactoryMethod('touch/longclick'), + 'flick' => $this->touchCommandFactoryMethod('touch/flick') + ); + } + + protected function getSessionUrl() + { + return $this->url->ascend()->ascend(); + } + + private function touchCommandFactoryMethod($urlSegment) + { + $url = $this->getSessionUrl()->addCommand($urlSegment); + $self = $this; + return function ($jsonParameters, $commandUrl) use ($url, $self) { + if ((is_array($jsonParameters) && + !isset($jsonParameters['element'])) || + is_null($jsonParameters)) { + $jsonParameters['element'] = $self->getId(); + } + return new PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost($jsonParameters, $url); + }; + } + + /** + * Retrieves the tag name + * @return string + */ + public function name() + { + return strtolower(parent::name()); + } + + /** + * Generates an array that is structured as the WebDriver Object of the JSONWireProtocoll + * + * @return array + */ + public function toWebDriverObject() + { + return array('ELEMENT' => (string)$this->getId()); + } + + /** + * Get or set value of form elements. If the element already has a value, the set one will be appended to it. + * Created **ONLY** for keeping backward compatibility, since in selenium v2.42.0 it was removed + * The currently recommended solution is to use `$element->attribute('value')` + * @see https://code.google.com/p/selenium/source/detail?r=953007b48e83f90450f3e41b11ec31e2928f1605 + * @see https://code.google.com/p/selenium/source/browse/java/CHANGELOG + * + * @param string $newValue + * @return null|string + */ + public function value($newValue = NULL) + { + if ($newValue !== NULL) { + return parent::value($newValue); + } + + return $this->attribute('value'); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Object representing an HTTP response from the Selenium Server. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_Response +{ + /** + * @var array decoded response + */ + private $jsonResponse; + + /** + * @var array CURL info for the response. + */ + private $info; + + public function __construct($jsonResponse, $info) + { + $this->jsonResponse = $jsonResponse; + $this->info = $info; + } + + public function getValue() + { + if (isset($this->jsonResponse['value'])) { + return $this->jsonResponse['value']; + } + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function getURL() + { + $url = $this->info['url']; + $sessionId = $this->jsonResponse['sessionId']; + + // if url doesn't have sessionId included - append it manually + // this change was performed in selenium v2.34 + // @see https://code.google.com/p/selenium/issues/detail?id=6089 + // @see https://github.com/sebastianbergmann/phpunit-selenium/issues/265 + if (strpos($url, $sessionId) === FALSE) { + $url .= '/' . $sessionId; + } + + return new PHPUnit_Extensions_Selenium2TestCase_URL($url); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.9 + */ + +/** + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.8 + */ +class PHPUnit_Extensions_Selenium2TestCase_NoSeleniumException + extends PHPUnit_Extensions_Selenium2TestCase_Exception +{ +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Keeps a Session object shared between test runs to save time. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionStrategy_Shared + implements PHPUnit_Extensions_Selenium2TestCase_SessionStrategy +{ + private $original; + private $session; + private $mainWindow; + private $lastTestWasNotSuccessful = FALSE; + + public function __construct(PHPUnit_Extensions_Selenium2TestCase_SessionStrategy $originalStrategy) + { + $this->original = $originalStrategy; + } + + public function session(array $parameters) + { + if ($this->lastTestWasNotSuccessful) { + if ($this->session !== NULL) { + $this->session->stop(); + $this->session = NULL; + } + $this->lastTestWasNotSuccessful = FALSE; + } + if ($this->session === NULL) { + $this->session = $this->original->session($parameters); + $this->mainWindow = $this->session->windowHandle(); + } else { + $this->session->window($this->mainWindow); + } + return $this->session; + } + + public function notSuccessfulTest() + { + $this->lastTestWasNotSuccessful = TRUE; + } + + public function endOfTest(PHPUnit_Extensions_Selenium2TestCase_Session $session = NULL) + { + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Produces a new Session object shared for each test. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionStrategy_Isolated + implements PHPUnit_Extensions_Selenium2TestCase_SessionStrategy +{ + public function session(array $parameters) + { + $seleniumServerUrl = PHPUnit_Extensions_Selenium2TestCase_URL::fromHostAndPort($parameters['host'], $parameters['port']); + $driver = new PHPUnit_Extensions_Selenium2TestCase_Driver($seleniumServerUrl, $parameters['seleniumServerRequestsTimeout']); + $capabilities = array_merge($parameters['desiredCapabilities'], + array( + 'browserName' => $parameters['browserName'] + )); + $session = $driver->startSession($capabilities, $parameters['browserUrl']); + return $session; + } + + public function notSuccessfulTest() + { + } + + public function endOfTest(PHPUnit_Extensions_Selenium2TestCase_Session $session = NULL) + { + if ($session !== NULL) { + $session->stop(); + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Clicks ok on an alert popup. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Click + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Checks equality (same element on the page) with another DOM element. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Equals + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + /** + * @param array $parameter + */ + public function __construct($parameter, + PHPUnit_Extensions_Selenium2TestCase_URL $equalsResourceBaseUrl) + { + $this->jsonParameters = array(); + if (!($parameter instanceof PHPUnit_Extensions_Selenium2TestCase_Element)) { + throw new InvalidArgumentException("Elements can only test equality with other Element instances."); + } + $this->url = $equalsResourceBaseUrl->descend($parameter->getId()); + } + + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Class for implementing commands that just return a value + * (obtained with GET). + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericAccessor + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Class for implementing commands that just accomplishes an action (via POST). + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2012 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Get and set the element's value attribute. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Value + extends PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Keys +{ + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + throw new BadMethodCallException("JSON Wire Protocol only supports POST to /value now. To get the value of an element GET /attribute/:naem should be used and this object should never be involved."); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Retrieves an attribute of a DOM element. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Attribute + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + /** + * @param array $parameter + */ + public function __construct($parameter, + PHPUnit_Extensions_Selenium2TestCase_URL $attributeResourceBaseUrl) + { + $this->jsonParameters = array(); + $this->url = $attributeResourceBaseUrl->descend($parameter); + } + + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Retrieves the value of a CSS property. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCommand_Css + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + /** + * @param array $propertyName + */ + public function __construct($propertyName, + PHPUnit_Extensions_Selenium2TestCase_URL $cssResourceBaseUrl) + { + $this->jsonParameters = array(); + $this->url = $cssResourceBaseUrl->descend($propertyName); + } + + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Conditions for selecting a DOM element. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element + */ +class PHPUnit_Extensions_Selenium2TestCase_ElementCriteria extends ArrayObject +{ + public function __construct($strategy) + { + $this['using'] = $strategy; + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_ElementCriteria + */ + public function value($searchTarget) + { + $this['value'] = $searchTarget; + return $this; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.5 + */ + +/** + * Object representing a browser window. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.5 + * @method array size(array $size = null) Window size as array('width' => $x, 'height' => $y) + * @method array position(array $position = null) Window position as array('x' => $x, 'y' => $y) + * @method array maximize() Maximize window + */ +class PHPUnit_Extensions_Selenium2TestCase_Window extends PHPUnit_Extensions_Selenium2TestCase_CommandsHolder +{ + /** + * @return array class names + */ + protected function initCommands() + { + return array( + 'size' => 'PHPUnit_Extensions_Selenium2TestCase_StateCommand', + 'position' => 'PHPUnit_Extensions_Selenium2TestCase_StateCommand', + 'maximize' => 'PHPUnit_Extensions_Selenium2TestCase_ElementCommand_GenericPost', + ); + } + +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Driver for creating browser session with Selenium 2 (WebDriver API). + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_Driver +{ + private $seleniumServerUrl; + private $seleniumServerRequestsTimeout; + + public function __construct(PHPUnit_Extensions_Selenium2TestCase_URL $seleniumServerUrl, $timeout = 60) + { + $this->seleniumServerUrl = $seleniumServerUrl; + $this->seleniumServerRequestsTimeout = $timeout; + } + + public function startSession(array $desiredCapabilities, PHPUnit_Extensions_Selenium2TestCase_URL $browserUrl) + { + $sessionCreation = $this->seleniumServerUrl->descend("/wd/hub/session"); + $response = $this->curl('POST', $sessionCreation, array( + 'desiredCapabilities' => $desiredCapabilities + )); + $sessionPrefix = $response->getURL(); + + $timeouts = new PHPUnit_Extensions_Selenium2TestCase_Session_Timeouts( + $this, + $sessionPrefix->descend('timeouts'), + $this->seleniumServerRequestsTimeout * 1000 + ); + return new PHPUnit_Extensions_Selenium2TestCase_Session( + $this, + $sessionPrefix, + $browserUrl, + $timeouts + ); + } + + /** + * Performs an HTTP request to the Selenium 2 server. + * + * @param string $method 'GET'|'POST'|'DELETE'|... + * @param string $url + * @param array $params JSON parameters for POST requests + */ + public function curl($http_method, + PHPUnit_Extensions_Selenium2TestCase_URL $url, + $params = NULL) + { + $curl = curl_init($url->getValue()); + curl_setopt($curl, CURLOPT_TIMEOUT, $this->seleniumServerRequestsTimeout); + curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); + curl_setopt($curl, + CURLOPT_HTTPHEADER, + array( + 'Content-type: application/json;charset=UTF-8', + 'Accept: application/json;charset=UTF-8' + )); + + if ($http_method === 'POST') { + curl_setopt($curl, CURLOPT_POST, TRUE); + if ($params && is_array($params)) { + curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($params)); + } else { + curl_setopt($curl, CURLOPT_POSTFIELDS, ''); + } + curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE); + } else if ($http_method == 'DELETE') { + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); + } + + $rawResponse = trim(curl_exec($curl)); + if (curl_errno($curl)) { + throw new PHPUnit_Extensions_Selenium2TestCase_NoSeleniumException( + 'Error connection[' . curl_errno($curl) . '] to ' . + $url->getValue() . ': ' . curl_error($curl) + ); + } + $info = curl_getinfo($curl); + if ($info['http_code'] == 0) { + throw new PHPUnit_Extensions_Selenium2TestCase_NoSeleniumException(); + } + if ($info['http_code'] == 404) { + throw new BadMethodCallException("The command $url is not recognized by the server."); + } + curl_close($curl); + $content = json_decode($rawResponse, TRUE); + if ($info['http_code'] == 500) { + $message = ''; + if (isset($content['value']['message'])) { + $message .= $content['value']['message']; + } else { + $message .= "Internal server error while executing $http_method request at $url. Response: " . var_export($content, TRUE); + } + if (isset($content['value']['class'])) { + $message .= PHP_EOL . $content['value']['class']; + } + throw new PHPUnit_Extensions_Selenium2TestCase_WebDriverException($message, isset($content['status']) ? $content['status'] : 13); + } + return new PHPUnit_Extensions_Selenium2TestCase_Response($content, $info); + } + + public function execute(PHPUnit_Extensions_Selenium2TestCase_Command $command) + { + return $this->curl($command->httpMethod(), + $command->url(), + $command->jsonParameters()); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.12 + */ + +/** + * The WaitUntil implementation, inspired by Java and .NET clients + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.12 + * @see http://selenium.googlecode.com/svn/trunk/dotnet/src/WebDriver.Support/UI/WebDriverWait.cs + * @see http://selenium.googlecode.com/svn/trunk/java/client/src/org/openqa/selenium/support/ui/FluentWait.java + */ +class PHPUnit_Extensions_Selenium2TestCase_WaitUntil +{ + /** + * PHPUnit Test Case instance + * + * @var PHPUnit_Extensions_Selenium2TestCase + */ + private $_testCase; + + /** + * Default timeout, ms + * + * @var int + */ + private $_defaultTimeout = 0; + + /** + * The sleep interval between iterations, ms + * + * @var int + */ + private $_defaultSleepInterval = 500; + + /** + * @param PHPUnit_Extensions_Selenium2TestCase $testCase + */ + public function __construct(PHPUnit_Extensions_Selenium2TestCase $testCase) + { + $this->_testCase = $testCase; + } + + /** + * @param $callback Callback to run until it returns not null or timeout occurs + * @param null $timeout + * @return mixed + * @throws PHPUnit_Extensions_Selenium2TestCase_Exception + * @throws PHPUnit_Extensions_Selenium2TestCase_WebDriverException + */ + public function run($callback, $timeout = NULL) + { + if (!is_callable($callback)) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception('The valid callback is expected'); + } + + // if there was an implicit timeout specified - remember it and temporarily turn it off + $implicitWait = $this->_testCase->timeouts()->getLastImplicitWaitValue(); + if ($implicitWait) { + $this->_testCase->timeouts()->implicitWait(0); + } + + if (is_null($timeout)) { + $timeout = $this->_defaultTimeout; + } + + $timeout /= 1000; + + $endTime = microtime(TRUE) + $timeout; + + $lastException = NULL; + + while (TRUE) { + try { + $result = call_user_func($callback, $this->_testCase); + + if (!is_null($result)) { + if ($implicitWait) { + $this->_testCase->timeouts()->implicitWait($implicitWait); + } + + return $result; + } + } catch(Exception $e) { + $lastException = $e; + } + + if (microtime(TRUE) > $endTime) { + if ($implicitWait) { + $this->_testCase->timeouts()->implicitWait($implicitWait); + } + + $message = "Timed out after {$timeout} second" . ($timeout != 1 ? 's' : ''); + throw new PHPUnit_Extensions_Selenium2TestCase_WebDriverException($message, + PHPUnit_Extensions_Selenium2TestCase_WebDriverException::Timeout, $lastException); + } + + usleep($this->_defaultSleepInterval * 1000); + } + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Christian Becker + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since + */ + +/** + * Indicates an exception as a result of a non-sucessful WebDriver response status code. + * + * @package PHPUnit_Selenium + * @author Christian Becker + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since + */ +class PHPUnit_Extensions_Selenium2TestCase_WebDriverException extends PHPUnit_Extensions_Selenium2TestCase_Exception +{ + /* @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes */ + const Success = 0; + const NoSuchDriver = 6; + const NoSuchElement = 7; + const NoSuchFrame = 8; + const UnknownCommand = 9; + const StaleElementReference = 10; + const ElementNotVisible = 11; + const InvalidElementState = 12; + const UnknownError = 13; + const ElementIsNotSelectable = 15; + const JavaScriptError = 17; + const XPathLookupError = 19; + const Timeout = 21; + const NoSuchWindow = 23; + const InvalidCookieDomain = 24; + const UnableToSetCookie = 25; + const UnexpectedAlertOpen = 26; + const NoAlertOpenError = 27; + const ScriptTimeout = 28; + const InvalidElementCoordinates = 29; + const IMENotAvailable = 30; + const IMEEngineActivationFailed = 31; + const InvalidSelector = 32; + const SessionNotCreatedException = 33; + const MoveTargetOutOfBounds = 34; +}. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Christian Soronellas + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or sets the current URL of the window. + * + * @package PHPUnit_Selenium + * @author Christian Soronellas + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Keys + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($jsonParameters, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if ($jsonParameters === NULL) { + parent::__construct(NULL, $url); + } else { + $jsonParameters = $this->keysForText($jsonParameters); + parent::__construct($jsonParameters, $url); + } + } + + /** + * @return string + */ + public function httpMethod() + { + return 'POST'; + } + + /** + * Given a string returns an array of the characters that compose the string + * + * @param string $text + * @throws InvalidArgumentException + * @return array + */ + public function keysForText($text) + { + if (is_scalar($text)) { + return array('value' => preg_split('//u', (string) $text, -1, PREG_SPLIT_NO_EMPTY)); + } + if (is_array($text)) { + return $text; + } + throw new InvalidArgumentException('The "text" argument should be a string or an array of special characters!'); + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.13 + */ + +/** + * Sends session click command for emulating LEFT, MIDDLE or RIGHT mouse buttons + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.13 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Click + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + const LEFT = 0; + const MIDDLE = 1; + const RIGHT = 2; + + public function __construct($argument, PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (is_null($argument)) { + $jsonParameters = NULL; + } elseif (!is_scalar($argument) || !in_array($argument, array( + self::LEFT, self::RIGHT, self::MIDDLE + ))) { + throw new BadMethodCallException('Wrong parameter for click(): expecting 0, 1 or 2.'); + } else { + $jsonParameters = array('button' => $argument); + } + + parent::__construct($jsonParameters, $url); + } + + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or posts an attribute from/to the session (title, alert text, etc.) + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.9 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Location + extends PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAttribute +{ + + public function __construct($location, $commandUrl) + { + if ($location !== NULL) { + $jsonParameters = array('location' => $location); + } else { + $jsonParameters = NULL; + } + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets an attribute from the session (title, alert text, etc.) + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAccessor + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.3.2 + */ + +/** + * Sends a file to a RC + * Returns the FQ path to the transfered file + * + * @package PHPUnit_Selenium + * @author Kevin Ran + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.3.2 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_File + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + + /** + * @var + */ + private static $_zipArchive; + + public function __construct($argument, PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (!is_file($argument)) { + throw new BadMethodCallException("No such file: {$argument}"); + } + + $zipfile_path = $this->_zipArchiveFile($argument); + $contents = file_get_contents($zipfile_path); + + if ($contents === false) { + throw new Exception("Unable to read generated zip file: {$zipfile_path}"); + } + + $file = base64_encode($contents); + + parent::__construct(array('file' => $file), $url); + + unlink($zipfile_path); + } + + public function httpMethod() + { + return 'POST'; + } + + /** + * Creates a zip archive with the given file + * + * @param string $file_path FQ path to file + * @return string Generated zip file + */ + protected function _zipArchiveFile( $file_path ) + { + + // file MUST be readable + if( !is_readable( $file_path ) ) { + + throw new Exception( "Unable to read {$file_path}" ); + + } // if !file_data + + $filename_hash = sha1( time() . $file_path ); + $tmp_dir = $this->_getTmpDir(); + $zip_filename = "{$tmp_dir}{$filename_hash}.zip"; + $zip = $this->_getZipArchiver(); + + if ($zip->open($zip_filename, ZIPARCHIVE::CREATE) === FALSE) { + throw new Exception( "Unable to create zip archive: {$zip_filename}" ); + } + + $zip->addFile($file_path, basename($file_path)); + $zip->close(); + + return $zip_filename; + } + + /** + * Returns a runtime instance of a ZipArchive + * + * @return ZipArchive + */ + protected function _getZipArchiver() + { + // create ZipArchive if necessary + if (!static::$_zipArchive) { + static::$_zipArchive = new ZipArchive(); + } + + return static::$_zipArchive; + } + + /** + * Calls sys_get_temp_dir and ensures that it has a trailing slash + * ( behavior varies across systems ) + * + * @return string + */ + protected function _getTmpDir() + { + return rtrim(sys_get_temp_dir(), DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Clicks Ok on an alert popup. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_AcceptAlert + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Obtains the text of an alert, or types into a prompt. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_AlertText + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($argument, PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (is_string($argument)) { + $jsonParameters =array('text' => $argument); + } else if ($argument == NULL) { + $jsonParameters = NULL; + } else { + throw new BadMethodCallException('Wrong parameters for alertText().'); + } + parent::__construct($jsonParameters, $url); + } + + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or sets the current URL of the window. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Url + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($url, $commandUrl, PHPUnit_Extensions_Selenium2TestCase_URL $baseUrl) + { + if ($url !== NULL) { + $absoluteLocation = $baseUrl->jump($url)->getValue(); + $jsonParameters = array('url' => $absoluteLocation); + } else { + $jsonParameters = NULL; + } + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Changes the focus to a window. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Window + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($name, $commandUrl) + { + $jsonParameters = array('name' => $name); + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or posts an attribute from/to the session (title, alert text, etc.) + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.9 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAttribute + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.8 + */ + +/** + * Moves the mouse pointer. + * + * @author Giorgio Sironi + * @package PHPUnit_Selenium + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.8 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_MoveTo + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($element, + PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + if (!is_array($element)) { + $element = array( + 'element' => $element, + ); + } + + $validKeys = array( + 'element' => NULL, + 'xoffset' => NULL, + 'yoffset' => NULL, + ); + + $jsonParameters = array_intersect_key($element, $validKeys); + + if (isset($jsonParameters['element'])) { + if (!($jsonParameters['element'] instanceof PHPUnit_Extensions_Selenium2TestCase_Element)) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception('Only moving over an element is supported. Please pass a PHPUnit_Extensions_Selenium2TestCase_Element instance.'); + } + + $jsonParameters['element'] = $jsonParameters['element']->getId(); + } + + if (isset($jsonParameters['xoffset']) || isset($jsonParameters['yoffset'])) { + // @see https://github.com/sebastianbergmann/phpunit-selenium/pull/250#issuecomment-21308153 + // @see https://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/moveto + error_log('Even though this method is a part of the WebDriver Wire protocol it might be not supported by your browser yet'); + } + + parent::__construct($jsonParameters, $url); + } + + /** + * @return string + */ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Gets or posts an attribute from/to the session (title, alert text, etc.) + * + * @package PHPUnit_Selenium + * @author Jonathan Lipps + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.9 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Orientation + extends PHPUnit_Extensions_Selenium2TestCase_SessionCommand_GenericAttribute +{ + + public function __construct($orientation, $commandUrl) + { + if ($orientation !== NULL) { + $jsonParameters = array('orientation' => $orientation); + } else { + $jsonParameters = NULL; + } + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + if ($this->jsonParameters) { + return 'POST'; + } + return 'GET'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Andrew Krasichkov + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.3.2 + */ + +/** + * Get the log for a given log type. Log buffer is reset after each request. + * + * @package PHPUnit_Selenium + * @author Andrew Krasichkov + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.3.2 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Log + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($type, $commandUrl) + { + $jsonParameters = array('type' => $type); + parent::__construct($jsonParameters, $commandUrl); + } + + public function httpMethod() + { + return 'POST'; + } +}. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release + */ + +/** + * Gets the active element from the session + * + * @package PHPUnit_Selenium + * @author Marcel Erz + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Active + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + + public function __construct($jsonParameters, PHPUnit_Extensions_Selenium2TestCase_URL $url) + { + $url = $url->addCommand('element')->addCommand('active'); + parent::__construct($jsonParameters, $url); + } + + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * Clicks Cancel on an alert popup. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_DismissAlert + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.4 + */ + +/** + * Changes the focus to a frame. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.4 + */ +class PHPUnit_Extensions_Selenium2TestCase_SessionCommand_Frame + extends PHPUnit_Extensions_Selenium2TestCase_Command +{ + public function __construct($id, $commandUrl) + { + $jsonParameters = array( + 'id' => $this->extractId($id) + ); + + parent::__construct($jsonParameters, $commandUrl); + } + + /** + * @param $id + * @return array + */ + private function extractId($id) + { + if ($this->isElement($id)) { //selenium-element + return $id->toWebDriverObject(); + } + + //html-id or null + return $id; + } + + /** + * @param $id + * @return bool + */ + private function isElement($id) + { + return $id instanceof PHPUnit_Extensions_Selenium2TestCase_Element; + } + + public function httpMethod() + { + return 'POST'; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.0 + */ + +/** + * URL Value Object allowing easy concatenation. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.0 + */ +final class PHPUnit_Extensions_Selenium2TestCase_URL +{ + /** + * @var string + */ + private $value; + + /** + * @param string $value + */ + public function __construct($value) + { + $this->value = $value; + } + + /** + * @param string $host + * @param int port + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public static function fromHostAndPort($host, $port) + { + return new self("http://{$host}:{$port}"); + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + public function __toString() + { + return $this->getValue(); + } + + /** + * @param string $addition + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function descend($addition) + { + if ($addition == '') { + // if we're adding nothing, respect the current url's choice of + // whether or not to include a trailing slash; prevents inadvertent + // adding of slashes to urls that can't handle it + $newValue = $this->value; + } else { + $newValue = rtrim($this->value, '/') + . '/' + . ltrim($addition, '/'); + } + return new self($newValue); + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function ascend() + { + $lastSlash = strrpos($this->value, "/"); + $newValue = substr($this->value, 0, $lastSlash); + return new self($newValue); + } + + /** + * @return string + */ + public function lastSegment() + { + $segments = explode('/', $this->value); + return end($segments); + } + + /** + * @param string $command + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function addCommand($command) + { + return $this->descend($this->camelCaseToUnderScores($command)); + } + + /** + * @param string $newUrl + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + public function jump($newUrl) + { + if ($this->isAbsolute($newUrl)) { + return new self($newUrl); + } else { + return $this->descend($newUrl); + } + } + + private function camelCaseToUnderScores($string) + { + $string = preg_replace('/([A-Z]{1,1})/', ' \1', $string); + $string = strtolower($string); + return str_replace(' ', '_', $string); + } + + private function isAbsolute($urlValue) + { + return preg_match('/^(http|https):\/\//', $urlValue) > 0; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.6 + */ + +/** + * Specifies how to create Session objects for running tests. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.6 + */ +interface PHPUnit_Extensions_Selenium2TestCase_SessionStrategy +{ + /** + * @param array $parameters 'host' => Selenium Server machine + 'port' => Selenium Server port + 'browser' => a browser name + * 'browserUrl' => base URL to use during the test + */ + public function session(array $parameters); + + public function notSuccessfulTest(); + + public function endOfTest(PHPUnit_Extensions_Selenium2TestCase_Session $session = NULL); +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.12 + */ + +/** + * Class-mapper, that converts requested special key into correspondent Unicode character + * + * @package PHPUnit_Selenium + * @author Ivan Kurnosov + * @copyright 2010-2011 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + * @since Class available since Release 1.2.12 + * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value + */ +class PHPUnit_Extensions_Selenium2TestCase_KeysHolder +{ + private $_keys = array( + 'null' => "\xEE\x80\x80", + 'cancel' => "\xEE\x80\x81", + 'help' => "\xEE\x80\x82", + 'backspace' => "\xEE\x80\x83", + 'tab' => "\xEE\x80\x84", + 'clear' => "\xEE\x80\x85", + 'return' => "\xEE\x80\x86", + 'enter' => "\xEE\x80\x87", + 'shift' => "\xEE\x80\x88", + 'control' => "\xEE\x80\x89", + 'alt' => "\xEE\x80\x8A", + 'pause' => "\xEE\x80\x8B", + 'escape' => "\xEE\x80\x8C", + 'space' => "\xEE\x80\x8D", + 'pageup' => "\xEE\x80\x8E", + 'pagedown' => "\xEE\x80\x8F", + 'end' => "\xEE\x80\x90", + 'home' => "\xEE\x80\x91", + 'left' => "\xEE\x80\x92", + 'up' => "\xEE\x80\x93", + 'right' => "\xEE\x80\x94", + 'down' => "\xEE\x80\x95", + 'insert' => "\xEE\x80\x96", + 'delete' => "\xEE\x80\x97", + 'semicolon' => "\xEE\x80\x98", + 'equals' => "\xEE\x80\x99", + 'numpad0' => "\xEE\x80\x9A", + 'numpad1' => "\xEE\x80\x9B", + 'numpad2' => "\xEE\x80\x9C", + 'numpad3' => "\xEE\x80\x9D", + 'numpad4' => "\xEE\x80\x9E", + 'numpad5' => "\xEE\x80\x9F", + 'numpad6' => "\xEE\x80\xA0", + 'numpad7' => "\xEE\x80\xA1", + 'numpad8' => "\xEE\x80\xA2", + 'numpad9' => "\xEE\x80\xA3", + 'multiply' => "\xEE\x80\xA4", + 'add' => "\xEE\x80\xA5", + 'separator' => "\xEE\x80\xA6", + 'subtract' => "\xEE\x80\xA7", + 'decimal' => "\xEE\x80\xA8", + 'divide' => "\xEE\x80\xA9", + 'f1' => "\xEE\x80\xB1", + 'f2' => "\xEE\x80\xB2", + 'f3' => "\xEE\x80\xB3", + 'f4' => "\xEE\x80\xB4", + 'f5' => "\xEE\x80\xB5", + 'f6' => "\xEE\x80\xB6", + 'f7' => "\xEE\x80\xB7", + 'f8' => "\xEE\x80\xB8", + 'f9' => "\xEE\x80\xB9", + 'f10' => "\xEE\x80\xBA", + 'f11' => "\xEE\x80\xBB", + 'f12' => "\xEE\x80\xBC", + 'command' => "\xEE\x80\xBD", + ); + + public function specialKey($name) + { + $normalizedName = strtolower($name); + + if (!isset($this->_keys[$normalizedName])) { + throw new PHPUnit_Extensions_Selenium2TestCase_Exception("There is no special key '$name' defined"); + } + + return $this->_keys[$normalizedName]; + } +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + */ + + +/** + * Provides access to /element and /elements commands + * + * @package PHPUnit_Selenium + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @version Release: @package_version@ + * @link http://www.phpunit.de/ + */ +abstract class PHPUnit_Extensions_Selenium2TestCase_Element_Accessor + extends PHPUnit_Extensions_Selenium2TestCase_CommandsHolder +{ + + /** + * @param string $value e.g. 'container' + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function byClassName($value) + { + return $this->by('class name', $value); + } + + /** + * @param string $value e.g. 'div.container' + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function byCssSelector($value) + { + return $this->by('css selector', $value); + } + + /** + * @param string $value e.g. 'uniqueId' + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function byId($value) + { + return $this->by('id', $value); + } + + /** + * @param string $value e.g. 'Link text' + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function byLinkText($value) + { + return $this->by('link text', $value); + } + + /** + * @param string $value e.g. 'Link te' + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function byPartialLinkText($value) + { + return $this->by('partial link text', $value); + } + + /** + * @param string $value e.g. 'email_address' + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function byName($value) + { + return $this->by('name', $value); + } + + /** + * @param string $value e.g. 'body' + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function byTag($value) + { + return $this->by('tag name', $value); + } + + /** + * @param string $value e.g. '/div[@attribute="value"]' + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function byXPath($value) + { + return $this->by('xpath', $value); + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function element(PHPUnit_Extensions_Selenium2TestCase_ElementCriteria $criteria) + { + $value = $this->postCommand('element', $criteria); + return PHPUnit_Extensions_Selenium2TestCase_Element::fromResponseValue( + $value, $this->getSessionUrl()->descend('element'), $this->driver); + } + + /** + * @return array instances of PHPUnit_Extensions_Selenium2TestCase_Element + */ + public function elements(PHPUnit_Extensions_Selenium2TestCase_ElementCriteria $criteria) + { + $values = $this->postCommand('elements', $criteria); + $elements = array(); + foreach ($values as $value) { + $elements[] = + PHPUnit_Extensions_Selenium2TestCase_Element::fromResponseValue( + $value, $this->getSessionUrl()->descend('element'), $this->driver); + } + return $elements; + } + + /** + * @param string $strategy + * @return PHPUnit_Extensions_Selenium2TestCase_ElementCriteria + */ + public function using($strategy) + { + return new PHPUnit_Extensions_Selenium2TestCase_ElementCriteria($strategy); + } + + /** + * @return PHPUnit_Extensions_Selenium2TestCase_URL + */ + protected abstract function getSessionUrl(); + + /** + * @param string $strategy supported by JsonWireProtocol element/ command + * @param string $value + * @return PHPUnit_Extensions_Selenium2TestCase_Element + */ + private function by($strategy, $value) + { + return $this->element($this->using($strategy)->value($value)); + } + +} +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, 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. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE 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 SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit_Selenium + * @author Giorgio Sironi + * @copyright 2010-2013 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 1.2.2 + */ + +/** + * Object representing a ",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+L+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+L+"*(?:value|"+K+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){var b=g.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+L+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",P)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===g||a.ownerDocument===v&&t(v,a)?-1:b===g||b.ownerDocument===v&&t(v,b)?1:k?J(k,a)-J(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,h=[a],i=[b];if(!e||!f)return a===g?-1:b===g?1:e?-1:f?1:k?J(k,a)-J(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?la(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},g):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&D.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(ca,da),a[3]=(a[3]||a[4]||a[5]||"").replace(ca,da),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(ca,da).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+L+")"+a+"("+L+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(Q," ")+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=J(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(ca,da),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return W.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(ca,da).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:oa(function(){return[0]}),last:oa(function(a,b){return[b-1]}),eq:oa(function(a,b,c){return[0>c?c+b:c]}),even:oa(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:oa(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:oa(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:oa(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function sa(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function ta(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function ua(a,b,c){for(var d=0,e=b.length;e>d;d++)ga(a,b[d],c);return c}function va(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function wa(a,b,c,d,e,f){return d&&!d[u]&&(d=wa(d)),e&&!e[u]&&(e=wa(e,f)),ia(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||ua(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:va(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=va(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?J(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=va(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):H.apply(g,r)})}function xa(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=sa(function(a){return a===b},h,!0),l=sa(function(a){return J(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];f>i;i++)if(c=d.relative[a[i].type])m=[sa(ta(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return wa(i>1&&ta(m),i>1&&ra(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&xa(a.slice(i,e)),f>e&&xa(a=a.slice(e)),f>e&&ra(a))}m.push(c)}return ta(m)}function ya(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=F.call(i));s=va(s)}H.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&ga.uniqueSort(i)}return k&&(w=v,j=t),r};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=xa(b[c]),f[u]?d.push(f):e.push(f);f=A(a,ya(e,d)),f.selector=a}return f},i=ga.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(ca,da),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(ca,da),aa.test(j[0].type)&&pa(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&ra(j),!a)return H.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,aa.test(a)&&pa(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(K,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1; + +return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),ha=/^\s+/,ia=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,ja=/<([\w:]+)/,ka=/\s*$/g,ra={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sa=da(y),ta=sa.appendChild(y.createElement("div"));ra.optgroup=ra.option,ra.tbody=ra.tfoot=ra.colgroup=ra.caption=ra.thead,ra.th=ra.td;function ua(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ua(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function va(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wa(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xa(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function ya(a){var b=pa.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function za(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Aa(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Ba(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xa(b).text=a.text,ya(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!ga.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(ta.innerHTML=a.outerHTML,ta.removeChild(f=ta.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ua(f),h=ua(a),g=0;null!=(e=h[g]);++g)d[g]&&Ba(e,d[g]);if(b)if(c)for(h=h||ua(a),d=d||ua(f),g=0;null!=(e=h[g]);g++)Aa(e,d[g]);else Aa(a,f);return d=ua(f,"script"),d.length>0&&za(d,!i&&ua(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=da(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(la.test(f)){h=h||o.appendChild(b.createElement("div")),i=(ja.exec(f)||["",""])[1].toLowerCase(),l=ra[i]||ra._default,h.innerHTML=l[1]+f.replace(ia,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&ha.test(f)&&p.push(b.createTextNode(ha.exec(f)[0])),!k.tbody){f="table"!==i||ka.test(f)?""!==l[1]||ka.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ua(p,"input"),va),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ua(o.appendChild(f),"script"),g&&za(h),c)){e=0;while(f=h[e++])oa.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wa(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ua(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&za(ua(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ua(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fa,""):void 0;if(!("string"!=typeof a||ma.test(a)||!k.htmlSerialize&&ga.test(a)||!k.leadingWhitespace&&ha.test(a)||ra[(ja.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ia,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ua(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ua(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&na.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ua(i,"script"),xa),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ua(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,ya),j=0;f>j;j++)d=g[j],oa.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qa,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Ca,Da={};function Ea(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fa(a){var b=y,c=Da[a];return c||(c=Ea(a,b),"none"!==c&&c||(Ca=(Ca||m("