diff --git a/.gitattributes b/.gitattributes index 8de27b2d9..53c3f3747 100644 --- a/.gitattributes +++ b/.gitattributes @@ -8,9 +8,10 @@ /docker export-ignore /fixtures export-ignore /tests export-ignore -Makefile export-ignore codecov.yml export-ignore +e2e-test.sh export-ignore ecs.php export-ignore +Makefile export-ignore phpunit.xml.dist export-ignore psalm-baseline.xml export-ignore psalm.xml.dist export-ignore diff --git a/e2e-test.sh b/e2e-test.sh new file mode 100755 index 000000000..b8585ff85 --- /dev/null +++ b/e2e-test.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +set -e +# set -x + +####################################################################################################################### +# This script runs semi end-to-end tests for Mockery. +# +# It clones the repositories listed in the `projects` array, +# installs Mockery from the local filesystem, +# and runs PHPUnit for each PHP version listed in the `php_versions` array. +# +# This is useful to test Mockery against different versions of other projects and frameworks. +# +####################################################################################################################### + +php_versions=( + "8.2" + "8.3" +) + +projects=( + "Brain-WP/BrainMonkey" + "filp/whoops" + "laravel/framework" +) + +mockery_path="$(pwd)" + +resources_path="$mockery_path/../mockery-resources" + +mockery_branch=$(git -C "$mockery_path" rev-parse --abbrev-ref HEAD) +mockery_sha=$(git -C "$mockery_path" rev-parse HEAD | cut -c1-8) +mockery_version="dev-$mockery_branch#$mockery_sha" + +echo "===> Running e2e tests" +echo "PHP versions: [ ${php_versions[*]} ]" +echo "Test Projects: [ ${projects[*]} ]" +echo " " +echo "Mockery branch: $mockery_branch" +echo "Mockery SHA: $mockery_sha" +echo "Mockery version: $mockery_version" +echo "Mockery path: $mockery_path" +echo "Resource path: $resources_path" +echo " " + + +mkdir -p "$resources_path" || { echo "Failed to create directory $resources_path"; exit 1; } +cd "$resources_path" || { echo "Failed to change directory to $resources_path"; exit 1; } + +for project in "${projects[@]}" +do + project_path="$resources_path/$project" + + if [ ! -d "$project_path" ]; then + echo "Cloning $project to $project_path" + + git clone "git@github.com:$project.git" "$project_path" --depth=10 || { echo "Failed to clone $project"; exit 1; } + else + echo "Pulling $project" + + git -C "$project_path" fetch --depth=10 || { echo "Failed to fetch $project"; exit 1; } + + git -C "$project_path" pull || { echo "Failed to pull $project"; exit 1; } + fi + + cd "$project_path" || { echo "Failed to change directory to $project_path"; exit 1; } + + echo "Installing Mockery version $mockery_version" + + for php_version in "${php_versions[@]}" + do + echo "Running PHPUnit for PHP version $php_version" + + docker run -it --rm -v "$mockery_path":/opt/mockery -v "$project_path":/opt/workspace -w /opt/workspace ghcr.io/ghostwriter/php:"$php_version"-pcov sh -c "composer config repositories.local '{\"type\": \"path\", \"url\": \"/opt/mockery\"}' && composer require 'mockery/mockery:$mockery_version' --with-dependencies --ignore-platform-reqs --no-scripts --no-plugins --dev --no-interaction && php vendor/bin/phpunit" || { echo "Failed to run PHPUnit for $project PHP version $php_version"; exit 1; } + done +done + +rm -rf "$resources_path" || { echo "Failed to remove directory $resources_path"; exit 1; } diff --git a/library/Mockery/Exception/BadMethodCallException.php b/library/Mockery/Exception/BadMethodCallException.php index 6567c22c5..edc0c69d5 100644 --- a/library/Mockery/Exception/BadMethodCallException.php +++ b/library/Mockery/Exception/BadMethodCallException.php @@ -21,10 +21,6 @@ public function dismiss() $this->dismissed = true; // we sometimes stack them $previous = $this->getPrevious(); - if (! $previous instanceof Throwable) { - return; - } - if (! $previous instanceof self) { return; } diff --git a/library/Mockery/Expectation.php b/library/Mockery/Expectation.php index af4d3848e..ef3ff1b2c 100644 --- a/library/Mockery/Expectation.php +++ b/library/Mockery/Expectation.php @@ -928,22 +928,6 @@ protected function _matchArg($expected, &$actual) return true; } - if (is_string($expected) && is_object($actual)) { - $result = $actual instanceof $expected; - - if ($result) { - return true; - } - } - - if (is_object($expected)) { - $matcher = Mockery::getConfiguration()->getDefaultMatcher(get_class($expected)); - - if ($matcher !== null) { - $expected = new $matcher($expected); - } - } - if ($expected instanceof MatcherInterface) { return $expected->match($actual); } @@ -958,7 +942,17 @@ protected function _matchArg($expected, &$actual) return $expected->matches($actual); } - return false; + if (is_object($expected)) { + $matcher = Mockery::getConfiguration()->getDefaultMatcher(get_class($expected)); + + return $matcher === null ? false : $this->_matchArg(new $matcher($expected), $actual); + } + + if (is_object($actual) && is_string($expected) && $actual instanceof $expected) { + return true; + } + + return $expected == $actual; } /** @@ -971,7 +965,7 @@ protected function _matchArg($expected, &$actual) protected function _matchArgs($args) { for ($index = 0, $argCount = count($args); $index < $argCount; ++$index) { - $param =&$args[$index]; + $param = &$args[$index]; if (! $this->_matchArg($this->_expectedArgs[$index], $param)) { return false; diff --git a/library/Mockery/Mock.php b/library/Mockery/Mock.php index e52e7724c..71ad448a9 100644 --- a/library/Mockery/Mock.php +++ b/library/Mockery/Mock.php @@ -202,7 +202,7 @@ public function mockery_init(Container $container = null, $partialObject = null, $this->_mockery_instanceMock = $instanceMock; - $this->parentClass = get_parent_class($this); + $this->_mockery_parentClass = get_parent_class($this); } /** @@ -644,15 +644,15 @@ public function __isset($name) return false; } - if (!$this->parentClass) { + if (!$this->_mockery_parentClass) { return false; } - if (!method_exists($this->parentClass, '__isset')) { + if (!method_exists($this->_mockery_parentClass, '__isset')) { return false; } - return call_user_func($this->parentClass . '::__isset', $name); + return call_user_func($this->_mockery_parentClass . '::__isset', $name); } public function mockery_getExpectations() @@ -671,11 +671,11 @@ public function mockery_getExpectations() */ public function mockery_callSubjectMethod($name, array $args) { - if (!method_exists($this, $name) && $this->parentClass && method_exists($this->parentClass, '__call')) { - return call_user_func($this->parentClass . '::__call', $name, $args); + if (!method_exists($this, $name) && $this->_mockery_parentClass && method_exists($this->_mockery_parentClass, '__call')) { + return call_user_func($this->_mockery_parentClass . '::__call', $name, $args); } - return call_user_func_array($this->parentClass . '::' . $name, $args); + return call_user_func_array($this->_mockery_parentClass . '::' . $name, $args); } /** @@ -910,7 +910,7 @@ protected function _mockery_handleMethodCall($method, array $args) // noop - there is no hasPrototype method } - return call_user_func_array($this->parentClass . '::' . $method, $args); + return call_user_func_array($this->_mockery_parentClass . '::' . $method, $args); } $handler = $this->_mockery_findExpectedMethodHandler($method); @@ -930,13 +930,13 @@ protected function _mockery_handleMethodCall($method, array $args) return $this->_mockery_partial->{$method}(...$args); } - if ($this->_mockery_deferMissing && is_callable($this->parentClass . '::' . $method) - && (!$this->hasMethodOverloadingInParentClass() || ($this->parentClass && method_exists($this->parentClass, $method)))) { - return call_user_func_array($this->parentClass . '::' . $method, $args); + if ($this->_mockery_deferMissing && is_callable($this->_mockery_parentClass . '::' . $method) + && (!$this->hasMethodOverloadingInParentClass() || ($this->_mockery_parentClass && method_exists($this->_mockery_parentClass, $method)))) { + return call_user_func_array($this->_mockery_parentClass . '::' . $method, $args); } - if ($this->_mockery_deferMissing && $this->parentClass && method_exists($this->parentClass, '__call')) { - return call_user_func($this->parentClass . '::__call', $method, $args); + if ($this->_mockery_deferMissing && $this->_mockery_parentClass && method_exists($this->_mockery_parentClass, '__call')) { + return call_user_func($this->_mockery_parentClass . '::__call', $method, $args); } if ($method === '__toString') { @@ -946,7 +946,7 @@ protected function _mockery_handleMethodCall($method, array $args) return sprintf('%s#%s', self::class, spl_object_hash($this)); } - if ($this->_mockery_ignoreMissing && (\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed() || (!is_null($this->_mockery_partial) && method_exists($this->_mockery_partial, $method)) || is_callable($this->parentClass . '::' . $method))) { + if ($this->_mockery_ignoreMissing && (\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed() || (!is_null($this->_mockery_partial) && method_exists($this->_mockery_partial, $method)) || is_callable($this->_mockery_parentClass . '::' . $method))) { if ($this->_mockery_defaultReturnValue instanceof Undefined) { return $this->_mockery_defaultReturnValue->{$method}(...$args); } @@ -995,7 +995,7 @@ protected function mockery_getMethods() private function hasMethodOverloadingInParentClass() { // if there's __call any name would be callable - return is_callable($this->parentClass . '::aFunctionNameThatNoOneWouldEverUseInRealLife12345'); + return is_callable($this->_mockery_parentClass . '::aFunctionNameThatNoOneWouldEverUseInRealLife12345'); } /** diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 3a101fed5..f2feecb15 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -335,6 +335,7 @@ allowMockingNonExistentMethods disableReflectionCache enableReflectionCache + getDefaultMatcher getInternalClassMethodParamMap mockingMethodsUnnecessarilyAllowed reflectionCacheEnabled @@ -791,7 +792,6 @@ mockery_allocateOrder mockery_getGroups mockery_setGroup - new $matcher($expected) validate diff --git a/tests/Mockery/ExpectationTest.php b/tests/Mockery/ExpectationTest.php index 514815de3..7dab9dcf6 100644 --- a/tests/Mockery/ExpectationTest.php +++ b/tests/Mockery/ExpectationTest.php @@ -2158,6 +2158,20 @@ public function it_uses_a_matchers_to_string_method_in_the_exception_output() Mockery::close(); } + + public function testNonObjectEqualsExpectation() + { + $input = ['club_id' => 1, 'user_id' => 1, 'is_admin' => 1]; + + $foo = Mockery::mock(); + + $foo->shouldReceive('foo')->with($input)->andReturn('foobar'); + + // only sort the input to change the order but not the values. + ksort($input); + + self::assertSame('foobar', $foo->foo($input)); + } } interface IWater