From 78b264017bb4bbb5f2077184d0ecfec8a06ab3d8 Mon Sep 17 00:00:00 2001
From: Luca Tumedei
Date: Tue, 12 Mar 2024 16:35:05 +0100
Subject: [PATCH] feat(Traits/UopzFunctions) add trait and documentation
---
docs/traits/UopzFunctions.md | 1305 +++++++++++++++++
mkdocs.yml | 6 +
src/Traits/UopzFunctions.php | 504 +++++++
tests/_data/uopz-test/functions.php | 69 +
tests/_data/uopz-test/global-classes.php | 115 ++
tests/_data/uopz-test/namespaced-classes.php | 105 ++
tests/_support/Traits/UopzFunctions.php | 202 ---
.../WPBrowser/Command/DevInfoTest.php | 8 +-
.../WPBrowser/Command/DevStartTest.php | 8 +-
.../WPBrowser/Command/DevStopTest.php | 8 +-
.../lucatume/WPBrowser/Command/RunAllTest.php | 8 +-
.../WPBrowser/Events/DispatcherTest.php | 2 +-
.../WPBrowser/Events/Module/WPDbTest.php | 7 +-
.../WPBrowser/Events/Module/WPQueriesTest.php | 3 +-
.../Extension/BuiltInServerControllerTest.php | 8 +-
.../Extension/ChromeDriverControllerTest.php | 6 +-
.../Extension/DockerComposeControllerTest.php | 28 +-
.../Extension/EventDispatcherBridgeTest.php | 10 +-
.../ManagedProcess/ChromedriverTest.php | 10 +-
.../ManagedProcess/PhpBuiltInServerTest.php | 6 +-
.../Process/Protocol/ControlTest.php | 6 +-
.../Process/Protocol/ResponseTest.php | 7 +-
.../WPBrowser/Project/PluginProjectTest.php | 2 +-
.../WPBrowser/Project/ThemeProjectTest.php | 2 +-
.../WPBrowser/Traits/UopzFunctionsTest.php | 1253 ++++++++++++++++
.../Utils/ChromedriverInstallerTest.php | 36 +-
.../lucatume/WPBrowser/Utils/ComposerTest.php | 16 +-
.../WordPress/Database/MysqlDatabaseTest.php | 7 +-
.../WordPress/Database/SqliteDatabaseTest.php | 12 +-
.../InstallationState/ConfiguredTest.php | 2 +-
.../InstallationState/EmptyDirTest.php | 2 +-
.../InstallationState/MultisiteTest.php | 2 +-
.../InstallationState/ScaffoldedTest.php | 6 +-
.../InstallationState/SingleTest.php | 8 +-
.../WPBrowser/WordPress/InstallationTest.php | 7 +-
.../WordPress/WpConfigFileGeneratorTest.php | 4 +-
tests/wploadersuite/AjaxTest.php | 2 +-
37 files changed, 3470 insertions(+), 322 deletions(-)
create mode 100644 docs/traits/UopzFunctions.md
create mode 100644 src/Traits/UopzFunctions.php
create mode 100644 tests/_data/uopz-test/functions.php
create mode 100644 tests/_data/uopz-test/global-classes.php
create mode 100644 tests/_data/uopz-test/namespaced-classes.php
delete mode 100644 tests/_support/Traits/UopzFunctions.php
create mode 100644 tests/unit/lucatume/WPBrowser/Traits/UopzFunctionsTest.php
diff --git a/docs/traits/UopzFunctions.md b/docs/traits/UopzFunctions.md
new file mode 100644
index 000000000..14e85ab40
--- /dev/null
+++ b/docs/traits/UopzFunctions.md
@@ -0,0 +1,1305 @@
+This trait provides a set of methods to manipulate functions, methods and class attributes using the [uopz][1] PHP
+extension.
+
+!!! warning
+
+ This test trait requires the `uopz` PHP extension.
+
+ See the [Installing the extension locally](#installing-the-extension-locally) section of this page for more information about how to do that.
+ If you need to install the extension in a CI environment, see the [Installing the extension in CI](#installing-the-extension-in-ci) section of this page.
+
+ If the `uopz` extension is not installed, test methods using methods from the `UopzFunctions` trait will be marked as skipped.
+
+### Why require an extension?
+
+Why use a PHP extension instead of a user-land solution, i.e. a PHP library that does not require installing an
+extension?
+
+I've written such a solution myself, [function-mocker][3], but have grown frustrated with its limitations, and the
+limitation of other similar solutions.
+
+All user-land, monkey-patching, pure PHP solutions rely on [stream-wrapping][4].
+This is a very powerful feature that this project uses for some of its functionality, but it has a drawbacks when used
+extensively for monkey-patching functions and methods:
+
+* the files patch must be included **after** the library loaded
+* the files have to patched, or patched and cached, on each run
+* there are some random and difficult to track issues introduced by how function and method patching works; e.g.
+ functions manipulating values by reference will not work as expected
+* some constants like `__METHOD__` and `__FUNCTION__` will not work as expected in the patched files
+* monkey-patching code will be "inserted" in the function stack, lengthening the stack trace and making it very
+ difficult to debug
+* all this processing together with [XDebug][9] spells doom for the performance of the test suite
+
+The `uopz` extension is a **solid and fast** solution that has been created and maintained by people that know PHP
+internals and the PHP language very well that has **none of the drawbacks** of the above-mentioned solutions.
+
+It is just a better tool for the job.
+
+### Installing the extension locally
+
+=== "Windows"
+
+ * Locate your `php.ini` file:
+ ```powershell
+ php --ini
+ ```
+ * Download the latest `DLL` stable version of the extension from the [releases page][2]. You'll likely need the `NTS x64` version.
+ * Unzip the file and copy the `php_uopz.dll` file to the `ext` folder of your PHP installation. If your `php.ini` file is located at `C:\tools\php81\php.ini`, the extensions directory will be located at `C:\tools\php81\ext`.
+ * Edit your `php.ini` file and add the following line to enable and configure the extension:
+ ```ini
+ extension=uopz
+ uopz.exit=1
+ ```
+ * Make sure the extension is correctly installed by running `php -m` and making sure the `uopz` extension appears in the list of extensions.
+
+ You can find more information about installing PHP extensions on Windows in the [PHP manual][6] and in [the `uopz` extension install guide][7].
+
+=== "Linux"
+
+ * Use the `pecl` command to install the extension:
+ ```bash
+ pecl install uopz
+ ```
+ * Configure the extension to ensure it will allow `exit` and `die` calls to terminate the script execution.
+ Add the following line to either the main PHP configuration file (`php.ini`), or a dedicated configuration file:
+ ```ini
+ uopz.exit=1
+ ```
+ * Make sure the extension is correctly installed by running `php -m` and making sure the `uopz` extension appears in the list of extensions.
+
+ Alternatively, you can build the extension from source as detailed in [the `uopz` extension install guide][7].
+
+=== "MacOS"
+
+ * Use the `pecl` command to install the extension:
+ ```bash
+ pecl install uopz
+ ```
+ * Configure the extension to ensure it will allow `exit` and `die` calls to terminate the script execution.
+ Add the following line to either the main PHP configuration file (`php.ini`), or a dedicated configuration file:
+ ```ini
+ uopz.exit=1
+ ```
+ * Make sure the extension is correctly installed by running `php -m` and making sure the `uopz` extension appears in the list of extensions.
+
+ Alternatively, you can build the extension from source as detailed in [the `uopz` extension install guide][7].
+
+### Installing the extension in CI
+
+Depending on your Continuous Integration (CI) solution of choice, the configuration required to install and set up
+the `uopz` extensions will be different.
+
+As an example, here is how you can set up the `uopz` extension in a GitHub Actions job:
+
+```yaml
+- name: Setup PHP 8.1 with uopz
+uses: shivammathur/setup-php@v2
+with:
+ php-version: 8.1
+ extensions: uopz
+ ini-values: uopz.exit=1
+```
+
+[This project uses the very same setup][8].
+
+Most CI systems are based on Linux OSes: if you're not using GitHub Actions, you can reference to the
+Linux [local installation instructions](#installing-the-extension-locally) to set up and install the extension for your
+CI solution of choice.
+
+### Usage
+
+Include the `UopzFunctions` trait in your test class and use the methods provided by the trait to manipulate functions,
+methods and class attributes.
+
+```php
+setFunctionReturn('wp_create_nonce', 'super-secret-nonce');
+
+ $this->assertEquals('super-secret-nonce', wp_create_nonce('some-action'));
+ }
+}
+```
+
+The trait will take care of cleaning up all the modifications made to the functions, methods and class attributes after
+each test.
+
+You can use the `UopzFunctions` trait in test cases extending the `PHUnit\Framework\TestCase` class as well:
+
+```php
+setFunctionReturn('someFunction', 'mocked-value');
+
+ $this->assertEquals('mocked-value', someFunction());
+ }
+}
+```
+
+### Methods
+
+The `UopzFunctions` trait provides the following methods:
+
+#### setFunctionReturn
+
+`setFunctionReturn(string $function, mixed $value, bool $execute = false): void`
+
+Set the return value for the function `$function` to `$value`.
+
+If `$value` is a closure and `$execute` is `true`, then the return value will be the return value of the closure.
+
+```php
+setFunctionReturn('wp_generate_nonce', 'super-secret-nonce');
+
+ $this->assertEquals('super-secret-nonce', wp_create_nonce('some-action'));
+ }
+}
+```
+
+If `$value` is a closure, the original function can be called within the closure to relay the original return value:
+
+```php
+setFunctionReturn(
+ 'wp_generate_nonce',
+ fn(string $action) => $action === 'test' ? 'test-nonce' : wp_create_nonce($action),
+ true
+ );
+
+ $this->assertEquals('test-nonce', wp_create_nonce('test'));
+ $this->assertNotEquals('test-nonce', wp_create_nonce('some-other-action'));
+ }
+}
+```
+
+#### unsetFunctionReturn
+
+`unsetFunctionReturn(string $function): void`
+
+Unset the return value for the function `$function` previously set with [`setFunctionReturn`](#setfunctionreturn).
+
+You do not need to unset the return value for a function that was set with [`setFunctionReturn`](#setfunctionreturn)
+using `unsetFunctionReturn` explicitly: the trait will take care of cleaning up all the modifications made to the
+functions, methods and class attributes after each test.
+
+#### setMethodReturn
+
+`setMethodReturn(string $class, string $method, mixed $value, bool $execute = false): void`
+
+Sets the return value for the static or instance method `$method` of the class `$class` to `$value`.
+
+If `$value` is a closure and `$execute` is `true`, then the return value will be the return value of the closure.
+
+Magic methods like `__construct`, `__destruct`, `__call` and so on cannot be mocked using this method.
+See the [`setClassMock`](#setclassmock) method for more information about how to mock magic class methods.
+
+```php
+setMethodReturn(SomeLegacyClass::class, 'staticMethod', 'STATIC');
+ $this->setMethodReturn(SomeLegacyClass::class, 'instanceMethod', 'TEST');
+
+ $legacyClass = new SomeLegacyClass();
+
+ $this->assertEquals('STATIC', SomeLegacyClass::staticMethod());
+ $this->assertEquals('TEST', $legacyClass->instanceMethod());
+ }
+}
+```
+
+If `$value` is a closure, the original static or instance method can be called within the closure, with correctly
+bound `self` and `$this` context, to relay the original return value:
+
+```php
+setMethodReturn(
+ SomeLegacyClass::class,
+ 'raiseStaticFlag',
+ fn(bool $flag) => $flag ? 'STATIC' : self::raiseStaticFlag($flag),
+ true
+ );
+ $this->setMethodReturn(
+ SomeLegacyClass::class,
+ 'raiseFlag',
+ fn(bool $flag) => $flag ? 'TEST' : $this->raiseFlag($flag),
+ true
+ );
+
+ $legacyClass = new SomeLegacyClass();
+
+ $this->assertEquals('STATIC', SomeLegacyClass::raiseStaticFlag(true));
+ $this->assertEquals('static-flag-lowered', SomeLegacyClass::raiseStaticFlag(false));
+ $this->assertEquals('TEST', $legacyClass->raiseFlag(true));
+ $this->assertEquals('flag-lowered', $legacyClass->raiseFlag(false));
+ }
+}
+```
+
+#### unsetmethodreturn
+
+`unsetmethodreturn(string $class, string $method): void`
+
+Unset the return value for the static or instance method `$method` of the class `$class` previously set
+with [`setMethodReturn`](#setmethodreturn).
+
+You do not need to unset the return value for a method that was set with [`setMethodReturn`](#setmethodreturn)
+using `unsetMethodReturn` explicitly: the trait will take care of cleaning up all the modifications made to the
+functions, methods and class attributes after each test.
+
+#### setFunctionHook
+
+`setFunctionHook(string $function, Closure $hook): void`
+
+Execute `$hook` when entering the function `$function`.
+
+Hooks can be set on both internal and user-defined functions.
+
+```php
+setFunctionHook(
+ 'header',
+ function($header, bool $replace = true, int $response_code = 0) use (&$log): void {
+ $log[] = $header;
+ }
+ );
+
+ header('X-Plugin-Version: 1.0.0');
+ header('X-Plugin-REST-Enabled: 1');
+ header('X-Plugin-GraphQL-Enabled: 0');
+
+ $this->assertEquals([
+ [
+ 'X-Plugin-Version' => '1.0.0',
+ 'X-Plugin-REST-Enabled' => '1',
+ 'X-Plugin-GraphQL-Enabled' => '0'
+ ], $log);
+ }
+}
+```
+
+#### unsetFunctionHook
+
+`unsetFunctionHook(string $function): void`
+
+Unset the hook for the function `$function` previously set with [`setFunctionHook`](#setfunctionhook).
+
+You do not need to unset the hook for a function that was set with [`setFunctionHook`](#setfunctionhook)
+using `unsetFunctionHook` explicitly: the trait will take care of cleaning up all the modifications made to the
+functions, methods and class attributes after each test.
+
+#### setMethodHook
+
+`setMethodHook(string $class, string $method, Closure $hook): void`
+
+Execute `$hook` when entering the static or instance method `$method` of the class `$class`.
+
+The keywords `self` and `$this` will be correctly bound to the class and the class instance respectively.
+
+```php
+cachedItems === null){
+ $this->cachedItems = wp_remote_get('https://example.com/items');
+ }
+
+ return array_slice($this->cachedItems, $from, $count);
+ }
+}
+
+class MyTest extends WPTestCase
+{
+ use UopzFunctions;
+
+ public function test_can_set_method_hook()
+ {
+ $connections = 0;
+ $this->setMethodHook(
+ LegacyApiController::class,
+ 'connect',
+ function() use (&$connections): void {
+ $connections = count(self::$connections) + 1;
+ }
+ );
+ $itemsCacheHits = 0;
+ $this->setMethodHook(
+ LegacyApiController::class,
+ 'getItems',
+ function(int $count, int $from = 0) use (&$itemsCacheHit): bool {
+ if($this->cachedItems !== null){
+ $itemsCacheHits++;
+ }
+ }
+ );
+
+ $connectedController1 = LegacyApiController::connect();
+ $connectedController2 = LegacyApiController::connect();
+ $connectedController1->getItems(10, 0);
+ $connectedController1->getItems(10, 10);
+ $connectedController2->getItems(10, 0);
+ $connectedController2->getItems(10, 10);
+
+ $this->assertEquals(2, $connections);
+ $this->assertEquals(4, $itemsCacheHits);
+ }
+}
+```
+
+#### unsetMethodHook
+
+`unsetMethodHook(string $class, string $method): void`
+
+Unset the hook for the static or instance method `$method` of the class `$class` previously set
+with [`setMethodHook`](#setmethodhook).
+
+You do not need to unset the hook for a method that was set with [`seMethodHook`](#setmethodhook)
+using `unsetClassMethodHook` explicitly: the trait will take care of cleaning up all the modifications made to the
+functions, methods and class attributes after each test.
+
+#### setConstant
+
+`setConstant(string $constant, mixed $value): void`
+
+Set the constant `$constant` to the value `$value`.
+
+If the constant is not already defined, it will be defined and set to the value `$value`.
+
+```php
+setconstant('WP_ADMIN', true);
+ $this->setconstant('TEST_CONST', 23);
+
+ $this->assertTrue(wp_is_admin());
+ $this->assertEquals(23, TEST_CONST);
+ }
+}
+```
+
+#### unsetConstant
+
+`unsetConstant(string $constant): void`
+
+Unset an existing constant or restores the original value of the constant if set with [`setConstant`](#setconstant).
+
+```php
+assertTrue(is_admin());
+
+ $this->unsetConstant('WP_ADMIN');
+
+ $this->assertFalse(is_admin());
+ }
+}
+```
+
+You do not need to undefine a constant defined with [`setConstant`](#setconstant) using `unsetConstant` explicitly: the
+trait will take care of cleaning up all the modifications made to the functions, methods and class attributes after each
+test.
+
+#### setClassConstant
+
+`setClassConstant(string $class, string $constant, mixed $value): void`
+
+Set the constant `$constant` of the class `$class` to the value `$value`.
+
+If the class constant is not already defined, it will be defined and set to the value `$value`.
+
+```php
+setClassConstant(MyPlugin::class, 'VERSION', '23.89.0');
+ $this->setClassConstant(MyPlugin::class, 'NOT_EXISTING', 'TEST');
+
+ $this->assertEquals('23.89.0', MyPlugin::VERSION);
+ $this->assertEquals('TEST', MyPlugin::NOT_EXISTING);
+ }
+}
+```
+
+#### unsetClassConstant
+
+`unsetClassConstant(string $class, string $constant): void`
+
+Restore the constant `$constant` of the class `$class` to its original value or removes it if it was not defined.
+
+You do not need to undefine a constant defined with [`setClassConstant`](#setclassconstant)
+using `undefineClassConstant` explicitly: the trait will take care of cleaning up all the modifications made to the
+functions, methods and class attributes after each test.
+
+#### setClassMock
+
+`setClassMock(string $class, string|object $mock): void`
+
+Use `$mock` instead of `$class` when creating new instances of the class `$class`.
+
+This method allows you to override magic methods as well as you would do with a normal class extension.
+
+```php
+setClassMock(PaymentApi::class, MockPaymentApi::class);
+
+ $paymentApi = new PaymentApi();
+ $this->assertInstanceOf(MyPluginMock::class, $paymentApi);
+ $this->assertSame('23.89.0', $paymentApi::version());
+ }
+}
+```
+
+If you set the `$mock` to an object, then the same mock object will be used for all the new instances of the
+class `$class`:
+
+```php
+setClassMock(PaymentApi::class, $mockPaymentApi);
+
+ $api1 = new PaymentApi();
+ $this->assertSame($mockPaymentApi, $api1);
+ $this->assertSame([1, 23, 89], $api1->getIds());
+ $api2 = new PaymentApi();
+ $this->assertSame($mockPaymentApi, $api2);
+ $this->assertSame([1, 23, 89], $api2->getIds());
+ }
+}
+```
+
+The `$mock` class, or instance, is **not** required to be a subclass of the class `$class` by the trait; although it
+might be required from the code you're testing by means of type hinting.
+
+If the class or method you would like to set a mock for is `final`, then you can combine this method with
+the [`unsetClassFinalAttribute`](#unsetclassfinalattribute)
+and [`unsetMethodFinalAttribute`](#unsetmethodfinalattribute) methods to avoid the final attribute being set on the
+class:
+
+```php
+unsetClassFinalAttribute(LegacyPaymentApi::class);
+ $mockPaymentApi = new class extends LegacyPaymentApi {
+ public function getIds(){
+ return [1, 23, 89];
+ }
+ };
+ $this->setClassMock(LegacyPaymentApi::class, $mockPaymentApi);
+ $this->unsetMethodFinalAttribute(LegacyCacheController::class, 'get');
+ $mockCacheController = new class extends LegacyCacheController {
+ public function get(string $key){
+ return 'some-value';
+ }
+ };
+
+ $paymentApi = new LegacyPaymentApi();
+
+ $this->assertSame($mockPaymentApi, $paymentApi);
+ $this->assertSame([1, 23, 89], $paymentApi->getIds());
+
+ $cacheController = new LegacyCacheController();
+
+ $this->assertSame($mockCacheController, $cacheController);
+ $this->assertSame('some-value', $cacheController->get('some-key'));
+ }
+}
+```
+
+#### unsetClassMock
+
+`unsetClassMock(string $class): void`
+
+Remove the mock for the class `$class` previously set with `setMock`.
+
+```php
+setClassMock(MyPlugin::class, new MyPluginMock());
+
+ $this->assertInstanceOf(MyPluginMock::class, new MyPlugin());
+
+ $this->unsetClassMock(MyPlugin::class);
+
+ $this->assertInstanceOf(MyPlugin::class, new MyPlugin());
+ }
+}
+```
+
+You do not need to unset the mock for a class that was set with [`setClassMock`](#setclassmock) using `unsetClassMock`
+explicitly: the trait will take care of cleaning up all the modifications made to the functions, methods and class
+attributes after each test.
+
+#### unsetClassFinalAttribute
+
+`unsetClassFinalAttribute(string $class): void`
+
+Remove the `final` attribute from the class `$class`.
+
+```php
+post->createAndGet();
+
+ $this->unsetClassFinalAttribute(LegacyPaymentApi::class);
+
+ // The class is not final anymore; it can be extended for testing purposes.
+ $mockPaymentApi = new class extends LegacyPaymentApi {
+ public function getIds(){
+ return [1, 23, 89];
+ }
+ };
+
+ $this->assertSame([1, 23, 89], $mockPaymentApi->getIds());
+ }
+}
+```
+
+#### resetClassFinalAttribute
+
+`resetClassFinalAttribute(string $class): void`
+
+Reset the `final` attribute of the class `$class` previously removed with
+the [`unsetClassFinalAttribute`](#unsetclassfinalattribute) method.
+
+You do not need to restore the class final attribute for a class that was set
+with [`unsetClassFinalAttribute`](#unsetclassfinalattribute) using `setClassFinalAttribute` explicitly: the trait will
+take care of cleaning up all the modifications made to the functions, methods and class attributes after each test.
+
+#### unsetMethodFinalAttribute
+
+`unsetMethodFinalAttribute(string $class, string $method): void`
+
+Remove the `final` attribute from the static or instance method `$method` of the class `$class`.
+
+```php
+unsetMethodFinalAttribute(LegacyAjaxController::class, 'printResponseAndExit');
+
+ // Build a class to avoid the `printResponseAndExit` method from exiting.
+ $testLegacyAdminController = new class extends LegacyAjaxController {
+ public string $response = '';
+
+ public function printResponseAndExit(){
+ $this->response = $this->template->render('list', return: true);
+ return;
+ }
+ };
+
+ // Set up things for the test ...
+
+ $testLegacyAjaxController->printResponseAndExit();
+
+ $this->assertEquals('', $testLegacyAjaxController->response);
+ }
+}
+```
+
+#### restoreMethodFinalAttribute
+
+`restoreMethodFinalAttribute(string $class, string $method): void`
+
+Restore the `final` attribute of the method static or instance `$method` of the class `$class` previously removed with
+the [`unsetMethodFinalAttribute`](#unsetmethodfinalattribute) method.
+
+You do not need to restore the method final attribute for a method that was set
+with [`unsetMethodFinalAttribute`](#unsetmethodfinalattribute) using `restoreMethodFinalAttribute` explicitly: the trait
+will take care of cleaning up all the modifications made to the functions, methods and class attributes after each test.
+
+#### addClassMethod
+
+`addClassMethod(string $class, string $method, Closure $closure, bool $static = false): void`
+
+Add a `public` static (`$static = true`) or instance (`$static = false`) method to the class `$class` with the
+name `$method` and the code provided by the closure `$closure`.
+
+Differently from the [`setClassMock`](#setclassmock) method, this method will work on **already existing instances** of
+the class `$class`, not just new instances.
+
+The closure `$this` will be bound to the class instance.
+
+```php
+cache === null){
+ $this->cache = wp_remote_get('https://example.com/items');
+ $this->cacheCount = count($cache);
+ }
+
+ return array_slice($this->cache, $from, $count);
+ }
+}
+
+class MyTest extends WPTestCase
+{
+ use UopzFunctions;
+
+ public function test_can_add_class_method()
+ {
+ $controller = LegacySingletonController::getInstance();
+
+ $this->addClassMethod(
+ LegacySingletonController::class,
+ 'setCache',
+ function(array $cache): void {
+ $this->cache = $cache;
+ $this->cacheCount = count($cache);
+ }
+ );
+
+ // Set the singletong instance cache for testing purposes.
+ $controller->setCache(range(1,100));
+
+ $this->assertEquals([1,2,3], $controller->getItems(3, 0));
+ }
+```
+
+#### removeClassMethod
+
+`removeClassMethod(string $class, string $method): void`
+
+Remove the static or instance method `$method` added with [`addClassMethod`](#addclassmethod) from the class `$class`.
+
+You do not need to remove a method added with [`addClassMethod`](#addclassmethod),
+or [`addClassStaticMethod`](#addclassstaticmethod), using `removeClassMethod` explicitly: the trait will take care of
+cleaning up all the modifications made to the functions, methods and class attributes after each test.
+
+#### setObjectProperty
+
+`setObjectProperty(string|object $classOrObject, string $property, mixed $value): void`
+
+If `$classOrInstance` is a string, set the property `$property` of the class `$classOrObject` to the value `$value`.
+If `$classOrInstance` is an object, set the property `$property` of the object `$classOrObject` to the value `$value`.
+
+```php
+uuid = UUID::generate();
+ }
+
+ public function getHash(): string {
+ return wp_hash(serialize([
+ 'uuid' => $this->uuid
+ 'from' => $this->from,
+ 'to' => $this->to
+ ]));
+ }
+}
+
+class MyTest extends WPTestCase
+{
+ use UopzFunctions;
+
+ public function test_can_set_object_property()
+ {
+ $payment = new Payment('Bob', 'Alice');
+
+ $this->setObjectProperty($payment, 'uuid', '550e8400-e29b-41d4-a716-446655440000');
+
+ $this->assertEquals(wp_hash(serialize([
+ 'uuid' => '550e8400-e29b-41d4-a716-446655440000',
+ 'from' => 'Bob',
+ 'to' => 'Alice'
+ ])), $payment->getHash());
+ }
+}
+```
+
+You do not need to reset the property of an object that was set with `setObjectProperty` explicitly: the trait will take
+care of cleaning up all the modifications made to the functions, methods and class attributes after each test.
+
+#### getObjectProperty
+
+`getObjectProperty(object $object, string $property): mixed`
+
+Get the value of the static or instance property `$property` of the object `$object`.
+
+```php
+template = new Template();
+ }
+
+ // ...
+}
+
+class MyTest extends WPTestCase
+{
+ use UopzFunctions;
+
+ public function test_can_get_object_property()
+ {
+ $controller = new LegacyController();
+
+ $templateEngine $this->getObjectProperty($controller, 'template'));
+
+ // ... do something with the template ...
+ }
+}
+```
+
+#### getMethodStaticVariables
+
+`getMethodStaticVariables(string $class, string $method): array`
+
+Get the value of the static variables of the class `$class` and method `$method`.
+
+The method will work for both static and instance methods of the class `$class`.
+
+```php
+log(200, 'OK');
+ $requestLogger->log(403, 'Forbidden');
+ $requestLogger->log(200, 'OK');
+ $buffer = ob_get_clean();
+
+ $requestId = $this->getClassMethodStaticVariables(RequestLogger::class, 'log')['requestId'];
+
+ $this->assertEquals("Request $requestId: 200 OK\nRequest $requestId: 403 Forbidden\nRequest $requestId: 200 OK\n", $buffer);
+}
+```
+
+#### setMethodStaticVariables
+
+`setMethodStaticVariables(string $class, string $method, array $values): void`
+
+Set the static variablesof the class `$class` and method `$method` to the values `$values`.
+
+This will work on both static and instance methods.
+
+```php
+Item OneItem Two';
+ }
+}
+
+class MyTest extends WPTestCase
+{
+ use UopzFunctions;
+
+ public function test_can_set_class_method_static_variables()
+ {
+ $newValues = array_merge(
+ $this->getMethodStaticVariables(ListComponent::class, 'render'),
+ ['hash' => 'some-hash']
+ );
+ $this->setClassMethodStaticVariables( ListComponent::class, 'render', [
+ 'hash' => 'some-hash'
+ ]);
+
+ $component = new ListComponent();
+
+ $this->assertEquals(
+ '',
+ $component->render()
+ );
+ }
+}
+```
+
+You do not need to reset the static variable of a class method that was set with `setMethodStaticVariables` explicitly
+using `resetMethodStaticVariables` explicitly: the trait will take care of cleaning up all the modifications made to the
+functions, methods and class attributes after each test.
+
+#### resetMethodStaticVariables
+
+`resetMethodStaticVariables(string $class, string $method): void`
+
+Resets the static variables of the class `$class` method `$method` to their original values.
+
+#### setFunctionStaticVariable
+
+`setFunctionStaticVariables(string $function, string $variable, mixed $value): void`
+
+Set the static variable `$variable` of the function `$function` to the value `$value`.
+
+```php
+Some HTML
';
+
+ $rendered = true;
+
+ return $html;
+}
+
+class MyTest extends WPTestCase
+{
+ use UopzFunctions;
+
+ public function test_can_set_function_static_variables()
+ {
+ $this->setFunctionStaticVariables('renderScreen', ['rendered' => false]);
+
+ $this->assertEquals('Some HTML
', renderScreen());
+ }
+}
+```
+
+You do not need to reset the value of a function static variable set using the `resetFunctionStaticVariables` method
+explicitly: the trait will take care of cleaning up all the modifications made to the functions, methods and class
+attributes after each test.
+
+#### getFunctionStaticVariables
+
+`getFunctionStaticVariables(string $function, ): array`
+
+Get the value of the static variable `$variable` of the function `$function`.
+
+```php
+Some HTML';
+}
+
+class MyTest extends WPTestCase
+{
+ use UopzFunctions;
+
+ public function test_can_set_function_static_variables()
+ {
+ $screenHash = $this->getFunctionStaticVariables('renderScreen')['screenHash'];
+
+ $this->assertEquals('Some HTML
', renderScreen());
+ }
+}
+```
+
+#### resetFunctionStaticVariables
+
+`resetFunctionStaticVariables(string $function): void`
+
+Resets the static variables of the function `$function` set with the `setFunctionStaticVariables` method.
+
+#### addFunction
+
+`addFunction(string $function, Closure $closure): void`
+
+Add a global or namespaced function to the current scope.
+
+```php
+addFunction('myGlobalFunction', fn() => 23);
+ $this->addFunction('Acme\Project\namespacedFunction', fn() => 89);
+
+ $this->assertEquals(23, myGlobalFunction());
+ $this->assertEquals(89, Acme\Project\namespacedFunction());
+ }
+}
+```
+
+#### removeFunction
+
+`removeFunction(string $function): void`
+
+Removes the global or namespaced function `$function` from the current scope.
+This will work for functions defined using the [`addFunction`](#addfunction) method or defined elsewhere.
+
+```php
+addFunction('myGlobalFunction', fn() => 23);
+ $this->addFunction('Acme\Project\namespacedFunction', fn() => 89);
+
+ $this->removeFunction('some_plugin_function');
+ $this->removeFunction('Acme\Project\namespacedFunction');
+
+ // Added with addFunction.
+ $this->assertFalse(function_exists('myGlobalFunction');
+ $this->assertFalse(function_exists('Acme\Project\namespacedFunction');
+
+ $this->assertFalse(function_exists('some_plugin_function');
+ $this->assertFalse(function_exists('Another\Plugin\some_function');
+ }
+}
+```
+
+You do not need to remove a function added with [`addFunction`](#addfunction) using `removeFunction` explicitly: the
+trait will take care of cleaning up all the modifications made to the functions, methods and class attributes after each
+test.
+
+#### preventExit
+
+`preventExit(): void`
+
+Prevents `exit` or `die` calls executed after the method from terminating the PHP process calling `exit` or `die`.
+
+```php
+preventExit();
+
+ ob_start();
+ printAndDie();
+ $buffer = ob_get_clean();
+
+ $this->assertEquals('Some HTML', $buffer);
+ }
+}
+```
+
+#### restoreExit
+
+`allowExit(): void`
+
+Restores the original behavior of the `exit` and `die` functions.
+
+You do not need to restore the exit behavior for a exit that was prevented using [`preventExit`](#preventexit)
+using `allowExit` explicitly: the trait will take care of cleaning up all the modifications made to the functions,
+methods and class attributes after each test.
+
+[1]: https://www.php.net/manual/en/book.uopz.php
+
+[2]: https://pecl.php.net/package/uopz
+
+[3]: https://github.com/lucatume/function-mocker
+
+[4]: https://www.php.net/manual/en/class.streamwrapper.php
+
+[5]: https://blog.benoitblanchon.fr/build-php-extension-on-windows/
+
+[6]: https://www.php.net/manual/en/install.pecl.windows.php
+
+[7]: https://github.com/krakjoe/uopz/blob/master/INSTALL.md
+
+[8]: https://github.com/lucatume/wp-browser/blob/78a5b5a691170a27b807a75ae063131e1ba3a87e/.github/workflows/test.yaml
+
+[9]: https://xdebug.org
diff --git a/mkdocs.yml b/mkdocs.yml
index 765781ed1..5f4307231 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -44,6 +44,8 @@ nav:
- generate:wpxmlrpc: 'commands#generatewpxmlrpc'
- monkey:cache:path: 'commands#monkeycachepath'
- monkey:cache:clear: 'commands#monkeycacheclear'
+ - Testing Helpers:
+ - UopzFunctions trait: 'traits/UopzFunctions.md'
- Troubleshooting: 'troubleshooting.md'
- Sponsorship: 'https://github.com/sponsors/lucatume'
- Changelog: 'https://github.com/lucatume/wp-browser/blob/master/CHANGELOG.md'
@@ -90,6 +92,7 @@ theme:
- navigation.top
- toc.follow
- content.code.select
+ - content.tabs.link
logo: assets/logo.png
favicon: assets/favicon.png
palette:
@@ -109,6 +112,9 @@ markdown_extensions:
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
+ - admonition
+ - pymdownx.tabbed:
+ alternate_style: true
extra:
social:
- icon: fontawesome/solid/paper-plane
diff --git a/src/Traits/UopzFunctions.php b/src/Traits/UopzFunctions.php
new file mode 100644
index 000000000..b14f667a0
--- /dev/null
+++ b/src/Traits/UopzFunctions.php
@@ -0,0 +1,504 @@
+
+ */
+ private static array $uopzSetFunctionReturns = [];
+
+ /**
+ * @var array
+ */
+ private static array $uopzSetFunctionHooks = [];
+
+ /**
+ * @var array
+ */
+ private static array $uopzSetConstants = [];
+
+ /**
+ * @var array
+ */
+ private static array $uopzSetClassMocks = [];
+
+ /**
+ * @var array
+ */
+ private static array $uopzUnsetClassFinalAttribute = [];
+
+ /**
+ * @var array
+ */
+ private static array $uopzAddClassMethods = [];
+
+ /**
+ * @var array
+ */
+ private static array $uopzUnsetClassMethodFinalAttribute = [];
+
+ /**
+ * @var array
+ */
+ private static array $uopzSetObjectProperties = [];
+
+ /**
+ * @var array>
+ */
+ private static array $uopzSetMethodStaticVariables = [];
+
+ /**
+ * @var array>
+ */
+ private static array $uopzSetFunctionStaticVariables = [];
+
+ /**
+ * @var array
+ */
+ private static array $uopzAddedFunctions = [];
+
+ private static ?bool $uopzAllowExit = null;
+
+ protected function setFunctionReturn(string $function, mixed $value, bool $execute = false): void
+ {
+ if (!function_exists('uopz_set_return')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ uopz_set_return($function, $value, $execute);
+ self::$uopzSetFunctionReturns[$function] = true;
+ }
+
+ protected function unsetFunctionReturn(string $function): void
+ {
+ if (!isset(self::$uopzSetFunctionReturns[$function])) {
+ return;
+ }
+
+ uopz_unset_return($function);
+ unset(self::$uopzSetFunctionReturns[$function]);
+ }
+
+ protected function setMethodReturn(string $class, string $method, mixed $value, bool $execute = false): void
+ {
+ $classAndMethod = "$class::$method";
+ uopz_set_return($class, $method, $value, $execute);
+ self::$uopzSetFunctionReturns[$classAndMethod] = true;
+ }
+
+ protected function unsetMethodReturn(string $class, string $method): void
+ {
+ $classAndMethod = "$class::$method";
+
+ if (!isset(self::$uopzSetFunctionReturns[$classAndMethod])) {
+ return;
+ }
+
+ uopz_unset_return($class, $method);
+ unset(self::$uopzSetFunctionReturns[$classAndMethod]);
+ }
+
+ protected function setFunctionHook(string $function, Closure $hook): void
+ {
+ if (!function_exists('uopz_set_hook')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ uopz_set_hook($function, $hook);
+ self::$uopzSetFunctionHooks[$function] = true;
+ }
+
+ protected function unsetFunctionHook(string $function): void
+ {
+ if (!isset(self::$uopzSetFunctionHooks[$function])) {
+ return;
+ }
+
+ uopz_unset_hook($function);
+ unset(self::$uopzSetFunctionHooks[$function]);
+ }
+
+ protected function setMethodHook(string $class, string $method, Closure $hook): void
+ {
+ if (!function_exists('uopz_set_hook')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $classAndMethod = "$class::$method";
+ uopz_set_hook($class, $method, $hook);
+ self::$uopzSetFunctionHooks[$classAndMethod] = true;
+ }
+
+ protected function unsetMethodHook(string $class, string $method): void
+ {
+ $classAndMethod = "$class::$method";
+
+ if (!isset(self::$uopzSetFunctionHooks[$classAndMethod])) {
+ return;
+ }
+
+ uopz_unset_hook($class, $method);
+ unset(self::$uopzSetFunctionHooks[$classAndMethod]);
+ }
+
+ protected function setConstant(string $constant, mixed $value): void
+ {
+ if (!function_exists('uopz_redefine')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $previousValue = defined($constant) ? constant($constant) : '__NOT_PREVIOUSLY_DEFINED__';
+ if ($previousValue === '__NOT_PREVIOUSLY_DEFINED__') {
+ define($constant, $value);
+ } else {
+ uopz_redefine($constant, $value);
+ }
+ self::$uopzSetConstants[$constant] = $previousValue;
+ }
+
+ protected function unsetConstant(string $constant): void
+ {
+ if (!isset(self::$uopzSetConstants[$constant])) {
+ return;
+ }
+
+ $previousValue = self::$uopzSetConstants[$constant];
+
+ if ($previousValue !== '__NOT_PREVIOUSLY_DEFINED__') {
+ uopz_redefine($constant, $previousValue);
+ } else {
+ uopz_undefine($constant);
+ }
+ unset(self::$uopzSetConstants[$constant]);
+ }
+
+ protected function setClassConstant(string $class, string $constant, mixed $value): void
+ {
+ if (!function_exists('uopz_redefine')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $previousValue = defined("$class::$constant") ?
+ constant("$class::$constant")
+ : '__NOT_PREVIOUSLY_DEFINED__';
+ uopz_redefine($class, $constant, $value);
+ self::$uopzSetConstants["$class::$constant"] = $previousValue;
+ }
+
+ protected function unsetClassConstant(string $class, string $constant): void
+ {
+ if (!isset(self::$uopzSetConstants["$class::$constant"])) {
+ return;
+ }
+
+ $previousValue = self::$uopzSetConstants["$class::$constant"];
+
+ if ($previousValue !== '__NOT_PREVIOUSLY_DEFINED__') {
+ uopz_redefine($class, $constant, $previousValue);
+ } else {
+ uopz_undefine($class, $constant);
+ }
+ unset(self::$uopzSetConstants["$class::$constant"]);
+ }
+
+ protected function setClassMock(string $class, mixed $mock): void
+ {
+ if (!function_exists('uopz_set_mock')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ uopz_set_mock($class, $mock);
+ self::$uopzSetClassMocks[$class] = true;
+ }
+
+ protected function unsetClassMock(string $class): void
+ {
+ if (!isset(self::$uopzSetClassMocks[$class])) {
+ return;
+ }
+
+ uopz_unset_mock($class);
+ unset(self::$uopzSetClassMocks[$class]);
+ }
+
+ protected function unsetClassFinalAttribute(string $class): void
+ {
+ if (!function_exists('uopz_unset_return')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $flags = uopz_flags($class, '');
+ uopz_flags($class, '', $flags & ~ZEND_ACC_FINAL);
+ self::$uopzUnsetClassFinalAttribute[$class] = true;
+ }
+
+ protected function resetClassFinalAttribute(string $class): void
+ {
+ if (!isset(self::$uopzUnsetClassFinalAttribute[$class])) {
+ return;
+ }
+
+ $flags = uopz_flags($class, '');
+ uopz_flags($class, '', $flags | ZEND_ACC_FINAL);
+ unset(self::$uopzUnsetClassFinalAttribute[$class]);
+ }
+
+ protected function unsetMethodFinalAttribute(string $class, string $method): void
+ {
+ if (!function_exists('uopz_unset_return')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $flags = uopz_flags($class, $method);
+ uopz_flags($class, $method, $flags & ~ZEND_ACC_FINAL);
+ self::$uopzUnsetClassMethodFinalAttribute["$class::$method"] = true;
+ }
+
+ protected function resetMethodFinalAttribute(string $class, string $method): void
+ {
+ $classAndMethod = "$class::$method";
+ if (!isset(self::$uopzUnsetClassMethodFinalAttribute[$classAndMethod])) {
+ return;
+ }
+
+ $flags = uopz_flags($class, $method);
+ uopz_flags($class, $method, $flags | ZEND_ACC_FINAL);
+ unset(self::$uopzUnsetClassMethodFinalAttribute[$classAndMethod]);
+ }
+
+ protected function addClassMethod(string $class, string $method, Closure $closure, bool $static = false): void
+ {
+ if (!function_exists('uopz_add_function')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $flags = ZEND_ACC_PUBLIC;
+ if ($static) {
+ $flags |= ZEND_ACC_STATIC;
+ }
+ uopz_add_function($class, $method, $closure, $flags);
+ self::$uopzAddClassMethods["$class::$method"] = true;
+ }
+
+ protected function removeClassMethod(string $class, string $method): void
+ {
+ $classAndMethod = "$class::$method";
+ if (!isset(self::$uopzAddClassMethods[$classAndMethod])) {
+ return;
+ }
+
+ uopz_del_function($class, $method);
+ unset(self::$uopzAddClassMethods[$classAndMethod]);
+ }
+
+ protected function setObjectProperty(
+ string|object $classOrObject,
+ string $property,
+ mixed $value
+ ): void {
+ if (!function_exists('uopz_set_property')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $previousValue = uopz_get_property($classOrObject, $property);
+ uopz_set_property($classOrObject, $property, $value);
+ $id = is_string($classOrObject) ? $classOrObject : spl_object_hash($classOrObject);
+ self::$uopzSetObjectProperties["$id::$property"] = [$previousValue, $classOrObject];
+ }
+
+ protected function getObjectProperty(string|object $classOrObject, string $property): mixed
+ {
+ if (!function_exists('uopz_get_property')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ return uopz_get_property($classOrObject, $property);
+ }
+
+ protected function resetObjectProperty(string|object $classOrObject, string $property): void
+ {
+ $id = is_string($classOrObject) ? $classOrObject : spl_object_hash($classOrObject);
+
+ if (!isset(self::$uopzSetObjectProperties["$id::$property"])) {
+ return;
+ }
+
+ [$previousValue, $classOrObject] = self::$uopzSetObjectProperties["$id::$property"];
+ uopz_set_property($classOrObject, $property, $previousValue);
+ unset(self::$uopzSetObjectProperties["$id::$property"]);
+ }
+
+ /**
+ * @param array $values
+ */
+ protected function setMethodStaticVariables(string $class, string $method, array $values): void
+ {
+ if (!function_exists('uopz_set_static')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $currentValues = uopz_get_static($class, $method);
+
+ if (!isset(self::$uopzSetMethodStaticVariables["$class::$method"])) {
+ self::$uopzSetMethodStaticVariables["$class::$method"] = $currentValues;
+ }
+
+ uopz_set_static($class, $method, $values);
+ }
+
+ /**
+ * @return array
+ */
+ protected function getMethodStaticVariables(string $class, string $method): array
+ {
+ if (!function_exists('uopz_get_static')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ return uopz_get_static($class, $method);
+ }
+
+ protected function resetMethodStaticVariables(string $class, string $method): void
+ {
+ if (!isset(self::$uopzSetMethodStaticVariables["$class::$method"])) {
+ return;
+ }
+
+ $staticVariables = self::$uopzSetMethodStaticVariables["$class::$method"];
+ uopz_set_static($class, $method, $staticVariables);
+ unset(self::$uopzSetMethodStaticVariables["$class::$method"]);
+ }
+
+ /**
+ * @return array
+ */
+ protected function getFunctionStaticVariables(string $function): array
+ {
+ if (!function_exists('uopz_get_static')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ return uopz_get_static($function);
+ }
+
+ /**
+ * @param array $values
+ */
+ protected function setFunctionStaticVariables(string $function, array $values): void
+ {
+ if (!function_exists('uopz_set_static')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ $currentValues = uopz_get_static($function);
+
+ if (!isset(self::$uopzSetFunctionStaticVariables[$function])) {
+ self::$uopzSetFunctionStaticVariables[$function] = $currentValues;
+ }
+
+ uopz_set_static($function, array_merge($currentValues, $values));
+ }
+
+ protected function resetFunctionStaticVariables(string $function): void
+ {
+ if (!isset(self::$uopzSetFunctionStaticVariables[$function])) {
+ return;
+ }
+
+ $staticVariables = self::$uopzSetFunctionStaticVariables[$function];
+ uopz_set_static($function, $staticVariables);
+ unset(self::$uopzSetFunctionStaticVariables[$function]);
+ }
+
+ protected function addFunction(string $function, Closure $handler): void
+ {
+ if (!function_exists('uopz_add_function')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ self::$uopzAddedFunctions[$function] = true;
+ uopz_add_function($function, $handler);
+ }
+
+ protected function removeFunction(string $function): void
+ {
+ if (!function_exists('uopz_del_function')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ if (!isset(self::$uopzAddedFunctions[$function])) {
+ return;
+ }
+
+ uopz_del_function($function);
+ unset(self::$uopzAddedFunctions[$function]);
+ }
+
+ protected function preventExit(): void
+ {
+ if (!function_exists('uopz_allow_exit')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ uopz_allow_exit(false);
+ self::$uopzAllowExit = false;
+ }
+
+ protected function allowExit(): void
+ {
+ if (self::$uopzAllowExit === true) {
+ return;
+ }
+
+ if (!function_exists('uopz_allow_exit')) {
+ $this->markTestSkipped('This test requires the uopz extension');
+ }
+
+ uopz_allow_exit(true);
+ self::$uopzAllowExit = true;
+ }
+
+ /**
+ * @after
+ */
+ public function resetUopzAlterations(): void
+ {
+ foreach (self::$uopzSetFunctionReturns as $function => $k) {
+ $this->unsetFunctionReturn($function);
+ }
+
+ foreach (self::$uopzSetFunctionHooks as $function => $k) {
+ $this->unsetFunctionHook($function);
+ }
+
+ foreach (self::$uopzSetConstants as $constant => $k) {
+ $this->unsetConstant($constant);
+ }
+
+ foreach (self::$uopzSetClassMocks as $class => $k) {
+ $this->unsetClassMock($class);
+ }
+
+ foreach (self::$uopzSetObjectProperties as $idAndProperty => [$previousValue, $classOrObject]) {
+ [, $property] = explode('::', $idAndProperty);
+ $this->resetObjectProperty($classOrObject, $property);
+ }
+
+ foreach (self::$uopzSetMethodStaticVariables as $classAndMethod => $values) {
+ [$class, $method] = explode('::', $classAndMethod);
+ $this->resetMethodStaticVariables($class, $method);
+ }
+
+ foreach (self::$uopzSetFunctionStaticVariables as $function => $values) {
+ $this->resetFunctionStaticVariables($function);
+ }
+
+ foreach (self::$uopzAddedFunctions as $function => $k) {
+ $this->removeFunction($function);
+ }
+ }
+}
diff --git a/tests/_data/uopz-test/functions.php b/tests/_data/uopz-test/functions.php
new file mode 100644
index 000000000..6356ad46b
--- /dev/null
+++ b/tests/_data/uopz-test/functions.php
@@ -0,0 +1,69 @@
+markTestSkipped('This test requires the uopz extension');
- }
-
- $replaced = [];
- foreach ($what as $key => $value) {
- if (function_exists($key)) {
- uopz_set_return($key, $value, is_callable($value));
- $replaced[$key] = 'func';
- continue;
- } elseif (strpos($key, '::')) {
- list($class, $method) = explode('::', $key, 2);
- uopz_set_return($class, $method, $value, is_callable($value));
- $replaced[$key] = 'static-method';
- continue;
- }
-
- throw new RuntimeException("{$key} is neither a function nor a static method.");
- }
-
- try {
- $do();
- } catch (Exception $e) {
- foreach ($replaced as $key => $type) {
- if ($type === 'func') {
- uopz_unset_return($key);
- } elseif ($type === 'static-method') {
- list($class, $method) = explode('::', $key, 2);
- uopz_unset_return($class, $method);
- }
- }
- throw $e;
- }
- }
-
- protected function uopzSetFunctionReturn(string $function, $return, bool $execute = false): void
- {
- if (!function_exists('uopz_set_return')) {
- $this->markTestSkipped('This test requires the uopz extension');
- }
-
- uopz_set_return($function, $return, $execute);
- self::$uopzSetFunctionReturns[] = $function;
- }
-
- protected function uopzSetStaticMethodReturn(
- string $class,
- string $method,
- mixed $return,
- bool $execute = false
- ): void {
- if (!function_exists('uopz_set_return')) {
- $this->markTestSkipped('This test requires the uopz extension');
- }
-
- uopz_set_return($class, $method, $return, $execute);
- self::$uopzSetStaticMethodReturns[] = $class . '::' . $method;
- }
-
- protected function uopzRedefineConstant(string $constant, string|int|bool|null $value): void
- {
- if (!function_exists('uopz_set_return')) {
- $this->markTestSkipped('This test requires the uopz extension');
- }
-
- uopz_redefine($constant, $value);
- $wasDefined = defined($constant);
- $previousValue = $wasDefined ? constant($constant) : null;
- self::$uopzRedefinedConstants[$constant] = [$wasDefined, $previousValue];
- }
-
- protected function uopzRedefinedClassConstant(string $class, string $constant, string|int|bool|null $value): void
- {
- if (!function_exists('uopz_set_return')) {
- $this->markTestSkipped('This test requires the uopz extension');
- }
-
- uopz_redefine($class, $constant, $value);
- $wasDefined = defined($class . '::' . $constant);
- $previousValue = $wasDefined ? constant($class . '::' . $constant) : null;
- self::$uopzRedefinedClassConstants[$class . '::' . $constant] = [$wasDefined, $previousValue];
- }
-
- /**
- * @after
- */
- public function uopzTearDown(): void
- {
- foreach (self::$uopzSetFunctionReturns as $function) {
- uopz_unset_return($function);
- }
- self::$uopzSetFunctionReturns = [];
-
- foreach (self::$uopzSetStaticMethodReturns as $classAndMethod) {
- [$class, $function] = explode('::', $classAndMethod, 2);
- uopz_unset_return($class, $function);
- }
- self::$uopzSetStaticMethodReturns = [];
-
- foreach (self::$uopzRedefinedConstants as $constant => [$wasDefined, $previousValue]) {
- uopz_undefine($constant);
- if ($wasDefined) {
- uopz_redefine($constant, $previousValue);
- }
- }
- self::$uopzRedefinedConstants = [];
-
- foreach (self::$uopzRedefinedClassConstants as $classAndConstant => [$wasDefined, $previousValue]) {
- [$class, $constant] = explode($classAndConstant, '::', 2);
- uopz_undefine($class, $constant);
- if ($wasDefined) {
- uopz_redefine($class, $constant, $previousValue);
- }
- }
- self::$uopzRedefinedClassConstants = [];
-
- foreach (self::$uopzSetMocks as $class) {
- uopz_unset_mock($class);
- }
- self::$uopzSetMocks = [];
- }
-
- protected function uopzUndefineConstant(string $constant): void
- {
- if (!function_exists('uopz_set_return')) {
- $this->markTestSkipped('This test requires the uopz extension');
- }
-
- if (!defined($constant)) {
- // The constant was not defined, let's make sure to reset during tear down.
- self::$uopzRedefinedConstants[$constant] = [false, null];
- return;
- }
-
- self::$uopzRedefinedConstants[$constant] = [true, constant($constant)];
-
- uopz_undefine($constant);
- }
-
- protected function uopzSetMock(string $class, string|object $mock): void
- {
- if (!function_exists('uopz_set_return')) {
- $this->markTestSkipped('This test requires the uopz extension');
- }
-
- self::$uopzSetMocks[] = $class;
- if ($mock instanceof Generator) {
- $mock = $mock->current();
- }
- uopz_set_mock($class, $mock);
- }
-
- protected function uopzUnsetMock(string $class): void
- {
- if (!function_exists('uopz_unset_mock')) {
- $this->markTestSkipped('This test requires the uopz extension');
- }
-
- $index = array_search($class, self::$uopzSetMocks, true);
-
- if ($index === false) {
- return;
- }
-
- unset(self::$uopzSetMocks[$index]);
- uopz_unset_mock($class);
- }
-
- protected function uopzUnsetFunctionReturn(string $string)
- {
- if (!function_exists('uopz_unset_return')) {
- $this->markTestSkipped('This test requires the uopz extension');
- }
-
- $index = array_search($string, self::$uopzSetFunctionReturns, true);
-
- if ($index === false) {
- return;
- }
-
- uopz_unset_return($string);
- unset(self::$uopzSetFunctionReturns[$index]);
- }
-}
diff --git a/tests/unit/lucatume/WPBrowser/Command/DevInfoTest.php b/tests/unit/lucatume/WPBrowser/Command/DevInfoTest.php
index f59ad8da6..19a35a694 100644
--- a/tests/unit/lucatume/WPBrowser/Command/DevInfoTest.php
+++ b/tests/unit/lucatume/WPBrowser/Command/DevInfoTest.php
@@ -9,7 +9,7 @@
use lucatume\WPBrowser\Extension\DockerComposeController;
use lucatume\WPBrowser\Extension\EventDispatcherBridge;
use lucatume\WPBrowser\Tests\Traits\ClassStubs;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\BufferedOutput;
use tad\Codeception\SnapshotAssertions\SnapshotAssertions;
@@ -48,7 +48,7 @@ public function should_print_information_about_each_service_extension_found_in_c
{
$builtInServerControllerBuildArgs = null;
- $this->uopzSetMock(BuiltInServerController::class,
+ $this->setClassMock(BuiltInServerController::class,
$this->makeEmptyClass(BuiltInServerController::class, [
'__construct' => function () use (&$builtInServerControllerBuildArgs) {
$builtInServerControllerBuildArgs = func_get_args();
@@ -64,7 +64,7 @@ public function should_print_information_about_each_service_extension_found_in_c
}
]));
$dockerComposeControllerBuildArgs = null;
- $this->uopzSetMock(DockerComposeController::class,
+ $this->setClassMock(DockerComposeController::class,
$this->makeEmptyClass(DockerComposeController::class, [
'__construct' => function () use (&$dockerComposeControllerBuildArgs) {
$dockerComposeControllerBuildArgs = func_get_args();
@@ -80,7 +80,7 @@ public function should_print_information_about_each_service_extension_found_in_c
}
]));
$chromeDriverControllerBuildArgs = null;
- $this->uopzSetMock(ChromeDriverController::class,
+ $this->setClassMock(ChromeDriverController::class,
$this->makeEmptyClass(ChromeDriverController::class, [
'__construct' => function () use (&$chromeDriverControllerBuildArgs) {
$chromeDriverControllerBuildArgs = func_get_args();
diff --git a/tests/unit/lucatume/WPBrowser/Command/DevStartTest.php b/tests/unit/lucatume/WPBrowser/Command/DevStartTest.php
index 7041927d8..0a8de1bed 100644
--- a/tests/unit/lucatume/WPBrowser/Command/DevStartTest.php
+++ b/tests/unit/lucatume/WPBrowser/Command/DevStartTest.php
@@ -9,7 +9,7 @@
use lucatume\WPBrowser\Extension\DockerComposeController;
use lucatume\WPBrowser\Extension\EventDispatcherBridge;
use lucatume\WPBrowser\Tests\Traits\ClassStubs;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\BufferedOutput;
@@ -46,7 +46,7 @@ public function should_start_each_service_extension_found_in_configuration(): vo
{
$builtInServerControllerBuildArgs = null;
$builtInServerControllerStarted = false;
- $this->uopzSetMock(BuiltInServerController::class,
+ $this->setClassMock(BuiltInServerController::class,
$this->makeEmptyClass(BuiltInServerController::class, [
'__construct' => function () use (&$builtInServerControllerBuildArgs) {
$builtInServerControllerBuildArgs = func_get_args();
@@ -57,7 +57,7 @@ public function should_start_each_service_extension_found_in_configuration(): vo
]));
$dockerComposeControllerBuildArgs = null;
$dockerComposeControllerStarted = false;
- $this->uopzSetMock(DockerComposeController::class,
+ $this->setClassMock(DockerComposeController::class,
$this->makeEmptyClass(DockerComposeController::class, [
'__construct' => function () use (&$dockerComposeControllerBuildArgs) {
$dockerComposeControllerBuildArgs = func_get_args();
@@ -68,7 +68,7 @@ public function should_start_each_service_extension_found_in_configuration(): vo
]));
$chromeDriverControllerBuildArgs = null;
$chromeDriverControllerStarted = false;
- $this->uopzSetMock(ChromeDriverController::class,
+ $this->setClassMock(ChromeDriverController::class,
$this->makeEmptyClass(ChromeDriverController::class, [
'__construct' => function () use (&$chromeDriverControllerBuildArgs) {
$chromeDriverControllerBuildArgs = func_get_args();
diff --git a/tests/unit/lucatume/WPBrowser/Command/DevStopTest.php b/tests/unit/lucatume/WPBrowser/Command/DevStopTest.php
index 7e76b20ab..353c7d022 100644
--- a/tests/unit/lucatume/WPBrowser/Command/DevStopTest.php
+++ b/tests/unit/lucatume/WPBrowser/Command/DevStopTest.php
@@ -9,7 +9,7 @@
use lucatume\WPBrowser\Extension\DockerComposeController;
use lucatume\WPBrowser\Extension\EventDispatcherBridge;
use lucatume\WPBrowser\Tests\Traits\ClassStubs;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use Symfony\Component\Console\Input\StringInput;
use Symfony\Component\Console\Output\BufferedOutput;
@@ -46,7 +46,7 @@ public function should_start_each_service_extension_found_in_configuration(): vo
{
$builtInServerControllerBuildArgs = null;
$builtInServerControllerStopped = false;
- $this->uopzSetMock(BuiltInServerController::class,
+ $this->setClassMock(BuiltInServerController::class,
$this->makeEmptyClass(BuiltInServerController::class, [
'__construct' => function () use (&$builtInServerControllerBuildArgs) {
$builtInServerControllerBuildArgs = func_get_args();
@@ -57,7 +57,7 @@ public function should_start_each_service_extension_found_in_configuration(): vo
]));
$dockerComposeControllerBuildArgs = null;
$dockerComposeControllerStopped = false;
- $this->uopzSetMock(DockerComposeController::class,
+ $this->setClassMock(DockerComposeController::class,
$this->makeEmptyClass(DockerComposeController::class, [
'__construct' => function () use (&$dockerComposeControllerBuildArgs) {
$dockerComposeControllerBuildArgs = func_get_args();
@@ -68,7 +68,7 @@ public function should_start_each_service_extension_found_in_configuration(): vo
]));
$chromeDriverControllerBuildArgs = null;
$crhomeDriverControllerStopped = false;
- $this->uopzSetMock(ChromeDriverController::class,
+ $this->setClassMock(ChromeDriverController::class,
$this->makeEmptyClass(ChromeDriverController::class, [
'__construct' => function () use (&$chromeDriverControllerBuildArgs) {
$chromeDriverControllerBuildArgs = func_get_args();
diff --git a/tests/unit/lucatume/WPBrowser/Command/RunAllTest.php b/tests/unit/lucatume/WPBrowser/Command/RunAllTest.php
index 419b4175d..8683a5aa4 100644
--- a/tests/unit/lucatume/WPBrowser/Command/RunAllTest.php
+++ b/tests/unit/lucatume/WPBrowser/Command/RunAllTest.php
@@ -9,7 +9,7 @@
use lucatume\WPBrowser\Adapters\Symfony\Component\Process\Process;
use lucatume\WPBrowser\Command\RunAll;
use lucatume\WPBrowser\Tests\Traits\ClassStubs;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use PHPUnit\Framework\Assert;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
@@ -49,7 +49,7 @@ public function should_invoke_codecept_bin_once_for_each_suite(): void
'getIterator' => fn() => yield from ["Running suite\n", "Done\n"],
'isSuccessful' => fn() => true,
];
- $this->uopzSetMock(Process::class, $this->makeEmptyClass(Process::class, $mockParams));
+ $this->setClassMock(Process::class, $this->makeEmptyClass(Process::class, $mockParams));
$this->uopzSetStaticMethodReturn(Configuration::class, 'suites', ['suite-1', 'suite-2', 'suite-3']);
$command = new RunAll();
@@ -85,7 +85,7 @@ public function should_return_1_if_any_suite_fails(int $failingSuite, string $ex
return $currentSuite++ !== $failingSuite;
},
];
- $this->uopzSetMock(Process::class, $this->makeEmptyClass(Process::class, $mockParams));
+ $this->setClassMock(Process::class, $this->makeEmptyClass(Process::class, $mockParams));
$this->uopzSetStaticMethodReturn(Configuration::class, 'suites', ['suite-1', 'suite-2', 'suite-3']);
$command = new RunAll();
@@ -103,7 +103,7 @@ public function should_return_1_if_any_suite_fails(int $failingSuite, string $ex
*/
public function should_return_1_if_failing_to_build_process(): void
{
- $this->uopzSetMock(Process::class,
+ $this->setClassMock(Process::class,
$this->makeEmptyClass(Process::class, [
'__construct' => fn() => throw new Exception('Failed to build process.')
]));
diff --git a/tests/unit/lucatume/WPBrowser/Events/DispatcherTest.php b/tests/unit/lucatume/WPBrowser/Events/DispatcherTest.php
index 05af2c6a9..811a3f5a2 100644
--- a/tests/unit/lucatume/WPBrowser/Events/DispatcherTest.php
+++ b/tests/unit/lucatume/WPBrowser/Events/DispatcherTest.php
@@ -5,7 +5,7 @@
use Codeception\Events;
use Codeception\Test\Unit;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
diff --git a/tests/unit/lucatume/WPBrowser/Events/Module/WPDbTest.php b/tests/unit/lucatume/WPBrowser/Events/Module/WPDbTest.php
index c375be975..8dc674a0a 100644
--- a/tests/unit/lucatume/WPBrowser/Events/Module/WPDbTest.php
+++ b/tests/unit/lucatume/WPBrowser/Events/Module/WPDbTest.php
@@ -9,12 +9,9 @@
use lucatume\WPBrowser\Module\Support\DbDump;
use lucatume\WPBrowser\Tests\Traits\LoopIsolation;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
-use lucatume\WPBrowser\WordPress\Database\SQLiteDatabase;
-use lucatume\WPBrowser\WordPress\Installation;
-use lucatume\WPBrowser\WordPress\InstallationState\InstallationStateInterface;
use PDO;
use RuntimeException;
@@ -143,7 +140,7 @@ public function it_should_throw_is_specified_dump_file_is_not_readable(): void
{
$root = FS::tmpDir('wpdb_', ['dump.sql' => 'SELECT 1']);
$filepath = $root . '/dump.sql';
- $this->uopzSetFunctionReturn('is_readable', static function (string $file) use ($filepath) {
+ $this->setFunctionReturn('is_readable', static function (string $file) use ($filepath) {
return $file !== $filepath && is_readable($file);
}, true);
diff --git a/tests/unit/lucatume/WPBrowser/Events/Module/WPQueriesTest.php b/tests/unit/lucatume/WPBrowser/Events/Module/WPQueriesTest.php
index 3dd524236..8d48d7e46 100644
--- a/tests/unit/lucatume/WPBrowser/Events/Module/WPQueriesTest.php
+++ b/tests/unit/lucatume/WPBrowser/Events/Module/WPQueriesTest.php
@@ -6,8 +6,7 @@
use Codeception\Lib\Di;
use Codeception\Lib\ModuleContainer;
use Codeception\Test\Unit;
-use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
diff --git a/tests/unit/lucatume/WPBrowser/Extension/BuiltInServerControllerTest.php b/tests/unit/lucatume/WPBrowser/Extension/BuiltInServerControllerTest.php
index 4323e5c66..b352887a1 100644
--- a/tests/unit/lucatume/WPBrowser/Extension/BuiltInServerControllerTest.php
+++ b/tests/unit/lucatume/WPBrowser/Extension/BuiltInServerControllerTest.php
@@ -10,7 +10,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Extension\BuiltInServerController;
use lucatume\WPBrowser\ManagedProcess\PhpBuiltInServer;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Composer;
use lucatume\WPBrowser\Utils\Random;
use stdClass;
@@ -71,7 +71,7 @@ public function _before()
$this->uopzSetStaticMethodReturn(Composer::class, 'binDir', $bin);
// Silence output.
$this->output = new Output(['verbosity' => Output::VERBOSITY_QUIET]);
- $this->uopzSetMock(Output::class, $this->output);
+ $this->setClassMock(Output::class, $this->output);
}
public function notArrayOfStringsProvider(): array
@@ -243,7 +243,7 @@ public function should_throw_if_config_env_is_not_associative_array_with_string_
*/
public function should_replace_cc_root_dir_placeholder_in_env_array(): void
{
- $this->uopzSetMock(PhpBuiltInServer::class, PhpBuiltInServerMock::class);
+ $this->setClassMock(PhpBuiltInServer::class, PhpBuiltInServerMock::class);
$config = [
'docroot' => __DIR__,
'env' => [
@@ -291,7 +291,7 @@ public function should_handle_php_built_in_server_lifecycle(): void
public function should_throw_if_pid_file_is_not_readable(): void
{
file_put_contents(PhpBuiltInServer::getPidFile(), '1233');
- $this->uopzSetFunctionReturn('file_get_contents', function (string $file): bool {
+ $this->setFunctionReturn('file_get_contents', function (string $file): bool {
if ($file === PhpBuiltInServer::getPidFile()) {
return false;
}
diff --git a/tests/unit/lucatume/WPBrowser/Extension/ChromeDriverControllerTest.php b/tests/unit/lucatume/WPBrowser/Extension/ChromeDriverControllerTest.php
index 036e1354d..5a156b6dd 100644
--- a/tests/unit/lucatume/WPBrowser/Extension/ChromeDriverControllerTest.php
+++ b/tests/unit/lucatume/WPBrowser/Extension/ChromeDriverControllerTest.php
@@ -10,7 +10,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Extension\ChromeDriverController;
use lucatume\WPBrowser\ManagedProcess\ChromeDriver;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Composer;
use stdClass;
use tad\Codeception\SnapshotAssertions\SnapshotAssertions;
@@ -41,7 +41,7 @@ public function _before()
$this->uopzSetStaticMethodReturn(Composer::class, 'binDir', $bin);
// Silence output.
$this->output = new Output(['verbosity' => Output::VERBOSITY_QUIET]);
- $this->uopzSetMock(Output::class, $this->output);
+ $this->setClassMock(Output::class, $this->output);
}
/**
@@ -205,7 +205,7 @@ public function should_handle_chromedriver_lifecycle(): void
public function should_throw_if_pid_file_is_not_readable(): void
{
file_put_contents(ChromeDriver::getPidFile(), '1233');
- $this->uopzSetFunctionReturn('file_get_contents', function (string $file): bool {
+ $this->setFunctionReturn('file_get_contents', function (string $file): bool {
if ($file === ChromeDriver::getPidFile()) {
return false;
}
diff --git a/tests/unit/lucatume/WPBrowser/Extension/DockerComposeControllerTest.php b/tests/unit/lucatume/WPBrowser/Extension/DockerComposeControllerTest.php
index 21e88448e..e1e45b81e 100644
--- a/tests/unit/lucatume/WPBrowser/Extension/DockerComposeControllerTest.php
+++ b/tests/unit/lucatume/WPBrowser/Extension/DockerComposeControllerTest.php
@@ -11,7 +11,7 @@
use Exception;
use lucatume\WPBrowser\Adapters\Symfony\Component\Process\Process;
use lucatume\WPBrowser\Tests\Traits\ClassStubs;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Composer;
use stdClass;
use Symfony\Component\Yaml\Yaml;
@@ -47,7 +47,7 @@ public function _before()
$this->uopzSetStaticMethodReturn(Composer::class, 'binDir', $bin);
// Silence output.
$this->output = new Output(['verbosity' => Output::VERBOSITY_QUIET]);
- $this->uopzSetMock(Output::class, $this->output);
+ $this->setClassMock(Output::class, $this->output);
}
/**
@@ -117,7 +117,7 @@ public function should_not_run_any_command_if_already_running(): void
{
file_put_contents(DockerComposeController::getRunningFile(), 'yes');
$constructed = 0;
- $this->uopzSetMock(
+ $this->setClassMock(
Process::class,
$this->makeEmptyClass(Process::class, [
'__construct' => static function (...$args) use (&$constructed) {
@@ -145,7 +145,7 @@ public function should_not_run_any_command_if_already_running(): void
public function should_up_stack_correctly(): void
{
$constructCommands = [];
- $this->uopzSetMock(
+ $this->setClassMock(
Process::class,
$this->makeEmptyClass(Process::class, [
'__construct' => static function ($command, ...$args) use (&$constructCommands) {
@@ -176,7 +176,7 @@ public function should_up_stack_correctly(): void
*/
public function should_throw_if_config_compose_file_is_not_valid_existing_file(): void
{
- $this->uopzSetMock(Process::class, $this->makeEmptyClass(Process::class, []));
+ $this->setClassMock(Process::class, $this->makeEmptyClass(Process::class, []));
$config = ['suites' => ['end2end'], 'compose-file' => 'not-a-file.yml'];
$options = [];
@@ -198,7 +198,7 @@ public function should_throw_if_config_compose_file_is_not_valid_existing_file()
*/
public function should_throw_if_config_env_file_is_not_valid_file(): void
{
- $this->uopzSetMock(Process::class, $this->makeEmptyClass(Process::class, []));
+ $this->setClassMock(Process::class, $this->makeEmptyClass(Process::class, []));
$config = ['suites' => ['end2end'], 'compose-file' => 'docker-compose.yml', 'env-file' => 'not-an-env-file'];
$options = [];
@@ -221,7 +221,7 @@ public function should_throw_if_config_env_file_is_not_valid_file(): void
public function should_correctly_handle_stack_lifecycle(): void
{
$constructed = 0;
- $this->uopzSetMock(
+ $this->setClassMock(
Process::class,
$this->makeEmptyClass(Process::class, [
'__construct' => static function () use (&$constructed) {
@@ -256,7 +256,7 @@ public function should_correctly_handle_stack_lifecycle(): void
*/
public function should_throw_if_docker_compose_start_fails(): void
{
- $this->uopzSetMock(
+ $this->setClassMock(
Process::class,
$this->makeEmptyClass(Process::class, [
'mustRun' => static function () {
@@ -283,7 +283,7 @@ public function should_throw_if_docker_compose_start_fails(): void
*/
public function should_throw_if_running_file_cannot_be_written(): void
{
- $this->uopzSetMock(Process::class, $this->makeEmptyClass(Process::class, []));
+ $this->setClassMock(Process::class, $this->makeEmptyClass(Process::class, []));
$config = ['suites' => ['end2end'], 'compose-file' => 'docker-compose.yml'];
$options = [];
@@ -293,7 +293,7 @@ public function should_throw_if_running_file_cannot_be_written(): void
$this->expectException(ExtensionException::class);
$this->expectExceptionMessage('Failed to write Docker Compose running file.');
- $this->uopzSetFunctionReturn('file_put_contents', false);
+ $this->setFunctionReturn('file_put_contents', false);
$extension->onModuleInit($this->make(SuiteEvent::class, ['getSuite' => $mockSuite]));
}
@@ -316,7 +316,7 @@ public function should_throw_if_stack_stopping_fails(): void
$this->assertFileExists(DockerComposeController::getRunningFile());
- $this->uopzSetMock(
+ $this->setClassMock(
Process::class,
$this->makeEmptyClass(Process::class, [
'mustRun' => static function () {
@@ -338,7 +338,7 @@ public function should_throw_if_stack_stopping_fails(): void
*/
public function should_throw_if_running_file_cannot_be_removed_while_stopping(): void
{
- $this->uopzSetMock(
+ $this->setClassMock(
Process::class,
$this->makeEmptyClass(Process::class, [
'stop' => 0
@@ -355,7 +355,7 @@ public function should_throw_if_running_file_cannot_be_removed_while_stopping():
$this->assertFileExists(DockerComposeController::getRunningFile());
- $this->uopzSetFunctionReturn('unlink', false);
+ $this->setFunctionReturn('unlink', false);
$this->expectException(ExtensionException::class);
$this->expectExceptionMessage('Failed to remove Docker Compose running file.');
@@ -370,7 +370,7 @@ public function should_throw_if_running_file_cannot_be_removed_while_stopping():
*/
public function should_produce_information_correctly(): void
{
- $this->uopzSetMock(
+ $this->setClassMock(
Process::class,
$this->makeEmptyClass(Process::class, [
'getOutput' => static function () {
diff --git a/tests/unit/lucatume/WPBrowser/Extension/EventDispatcherBridgeTest.php b/tests/unit/lucatume/WPBrowser/Extension/EventDispatcherBridgeTest.php
index 33413e4a1..59a413fc2 100644
--- a/tests/unit/lucatume/WPBrowser/Extension/EventDispatcherBridgeTest.php
+++ b/tests/unit/lucatume/WPBrowser/Extension/EventDispatcherBridgeTest.php
@@ -10,7 +10,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Events\Dispatcher;
use lucatume\WPBrowser\Extension\EventDispatcherBridge;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use PHPUnit\Framework\Assert;
use Psr\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;
@@ -27,7 +27,7 @@ class EventDispatcherBridgeTest extends Unit
public function should_throw_if_event_dispatcher_cannot_be_found_in_trace(): void
{
$mockTrace = [];
- $this->uopzSetFunctionReturn('debug_backtrace', $mockTrace);
+ $this->setFunctionReturn('debug_backtrace', $mockTrace);
$eventDispatcherBridge = new EventDispatcherBridge([], []);
@@ -49,7 +49,7 @@ public function should_set_global_event_dispatcher_to_application_one(): void
$mockTrace = [
['object' => $eventDispatcher]
];
- $this->uopzSetFunctionReturn('debug_backtrace', $mockTrace);
+ $this->setFunctionReturn('debug_backtrace', $mockTrace);
$this->uopzSetStaticMethodReturn(Dispatcher::class, 'getEventDispatcher', null);
$this->uopzSetStaticMethodReturn(Dispatcher::class,
'setEventDispatcher',
@@ -85,7 +85,7 @@ public function should_immediately_call_previous_event_dispatcher_listeners_on_t
$mockTrace = [
['object' => $eventDispatcher]
];
- $this->uopzSetFunctionReturn('debug_backtrace', $mockTrace);
+ $this->setFunctionReturn('debug_backtrace', $mockTrace);
$this->uopzSetStaticMethodReturn(Dispatcher::class, 'getEventDispatcher', $previousEventDispatcher);
$this->uopzSetStaticMethodReturn(Dispatcher::class, 'setEventDispatcher', null);
@@ -107,7 +107,7 @@ public function should_correctly_handle_the_case_where_the_previous_event_dispat
$mockTrace = [
['object' => $eventDispatcher]
];
- $this->uopzSetFunctionReturn('debug_backtrace', $mockTrace);
+ $this->setFunctionReturn('debug_backtrace', $mockTrace);
$this->uopzSetStaticMethodReturn(Dispatcher::class, 'getEventDispatcher', null);
$this->uopzSetStaticMethodReturn(Dispatcher::class,
'setEventDispatcher',
diff --git a/tests/unit/lucatume/WPBrowser/ManagedProcess/ChromedriverTest.php b/tests/unit/lucatume/WPBrowser/ManagedProcess/ChromedriverTest.php
index f6cdc5bfd..48a84ec7f 100644
--- a/tests/unit/lucatume/WPBrowser/ManagedProcess/ChromedriverTest.php
+++ b/tests/unit/lucatume/WPBrowser/ManagedProcess/ChromedriverTest.php
@@ -6,7 +6,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Adapters\Symfony\Component\Process\Process;
use lucatume\WPBrowser\Exceptions\RuntimeException;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Composer;
class ChromedriverTest extends Unit
@@ -94,7 +94,7 @@ public function should_throw_if_pid_is_not_integer_on_start(): void
'isRunning' => true,
'stop' => 5
]);
- $this->uopzSetMock(Process::class, $mockProcess);
+ $this->setClassMock(Process::class, $mockProcess);
$chromedriver = new ChromeDriver(3456, ['--url-base=wd/hub', '--headless']);
@@ -116,14 +116,14 @@ public function should_throw_if_pif_file_cannot_be_written_on_start(): void
'isRunning' => true,
'getPid' => 2389,
]);
- $this->uopzSetMock(Process::class, $mockProcess);
+ $this->setClassMock(Process::class, $mockProcess);
$chromedriver = new ChromeDriver(3456, ['--url-base=wd/hub', '--headless']);
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(ManagedProcessInterface::ERR_PID_FILE);
- $this->uopzSetFunctionReturn('file_put_contents', function (string $file): false|int {
+ $this->setFunctionReturn('file_put_contents', function (string $file): false|int {
return $file === ChromeDriver::getPidFile() ? false : 0;
}, true);
@@ -167,7 +167,7 @@ public function should_throw_if_pid_file_removal_fails(): void
$chromedriver = new ChromeDriver(3456, ['--url-base=wd/hub', '--headless']);
$chromedriver->start();
- $this->uopzSetFunctionReturn('unlink', false);
+ $this->setFunctionReturn('unlink', false);
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(ManagedProcessInterface::ERR_PID_FILE_DELETE);
diff --git a/tests/unit/lucatume/WPBrowser/ManagedProcess/PhpBuiltInServerTest.php b/tests/unit/lucatume/WPBrowser/ManagedProcess/PhpBuiltInServerTest.php
index d8d7157d3..8c10b7d51 100644
--- a/tests/unit/lucatume/WPBrowser/ManagedProcess/PhpBuiltInServerTest.php
+++ b/tests/unit/lucatume/WPBrowser/ManagedProcess/PhpBuiltInServerTest.php
@@ -7,7 +7,7 @@
use lucatume\WPBrowser\Adapters\Symfony\Component\Process\Process;
use lucatume\WPBrowser\Exceptions\RuntimeException;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Random;
class PhpBuiltinServerProcessMock extends Process
@@ -111,7 +111,7 @@ public function should_throw_if_env_is_not_associative_array(mixed $env): void
public function should_start_php_built_in_server_with_specified_workers(): void
{
$port = Random::openLocalhostPort();
- $this->uopzSetMock(Process::class, PhpBuiltinServerProcessMock::class);
+ $this->setClassMock(Process::class, PhpBuiltinServerProcessMock::class);
$server = new PhpBuiltInServer(__DIR__, $port, [
'PHP_CLI_SERVER_WORKERS' => 3,
@@ -130,7 +130,7 @@ public function should_start_php_built_in_server_with_specified_workers(): void
public function should_start_on_random_port_if_not_specified(): void
{
$port = Random::openLocalhostPort();
- $this->uopzSetMock(Process::class, PhpBuiltinServerProcessMock::class);
+ $this->setClassMock(Process::class, PhpBuiltinServerProcessMock::class);
$server = new PhpBuiltInServer(__DIR__, $port);
$server->start();
diff --git a/tests/unit/lucatume/WPBrowser/Process/Protocol/ControlTest.php b/tests/unit/lucatume/WPBrowser/Process/Protocol/ControlTest.php
index bda2e5eeb..890f29c46 100644
--- a/tests/unit/lucatume/WPBrowser/Process/Protocol/ControlTest.php
+++ b/tests/unit/lucatume/WPBrowser/Process/Protocol/ControlTest.php
@@ -7,7 +7,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Process\Protocol\Control;
use lucatume\WPBrowser\Process\Protocol\ProtocolException;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
class ControlTest extends Unit
{
@@ -152,7 +152,7 @@ public function testConfigsCodeceptionIfConfigIsSet(): void
public function testSetsCwdIfCodeceptionRootDirSet(): void
{
$changedDir = null;
- $this->uopzSetFunctionReturn('chdir', function (string $dir) use (&$changedDir) {
+ $this->setFunctionReturn('chdir', function (string $dir) use (&$changedDir) {
$changedDir = $changedDir ? chdir($dir) : $dir;
}, true);
$configCalled = false;
@@ -192,7 +192,7 @@ public function testThrowsIfCodeceptionRootDirDoesNotExist(): void
public function testSetsCwdIfSet(): void
{
$changedDir = null;
- $this->uopzSetFunctionReturn('chdir', function (string $dir) use (&$changedDir) {
+ $this->setFunctionReturn('chdir', function (string $dir) use (&$changedDir) {
$changedDir = $dir;
}, true);
$control = new Control([
diff --git a/tests/unit/lucatume/WPBrowser/Process/Protocol/ResponseTest.php b/tests/unit/lucatume/WPBrowser/Process/Protocol/ResponseTest.php
index c128025a6..373cacee1 100644
--- a/tests/unit/lucatume/WPBrowser/Process/Protocol/ResponseTest.php
+++ b/tests/unit/lucatume/WPBrowser/Process/Protocol/ResponseTest.php
@@ -2,11 +2,10 @@
namespace lucatume\WPBrowser\Process\Protocol;
-use CompileError;
use Exception;
-use lucatume\WPBrowser\Process\SerializableThrowable;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
use lucatume\WPBrowser\Opis\Closure\SerializableClosure;
+use lucatume\WPBrowser\Process\SerializableThrowable;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use PHPUnit\Framework\TestCase;
use Throwable;
@@ -77,7 +76,7 @@ public function testGetPayload(): void
$returnValue = "success";
$exitValue = 0;
$telemetry = ["memoryPeakUsage" => 123456];
- $this->uopzSetFunctionReturn('memory_get_peak_usage', 123456);
+ $this->setFunctionReturn('memory_get_peak_usage', 123456);
$response = new Response($returnValue, $exitValue, $telemetry);
diff --git a/tests/unit/lucatume/WPBrowser/Project/PluginProjectTest.php b/tests/unit/lucatume/WPBrowser/Project/PluginProjectTest.php
index b86e52721..e86e8d1db 100644
--- a/tests/unit/lucatume/WPBrowser/Project/PluginProjectTest.php
+++ b/tests/unit/lucatume/WPBrowser/Project/PluginProjectTest.php
@@ -7,7 +7,7 @@
use lucatume\WPBrowser\Exceptions\InvalidArgumentException;
use lucatume\WPBrowser\Tests\Traits\CliCommandTestingTools;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
diff --git a/tests/unit/lucatume/WPBrowser/Project/ThemeProjectTest.php b/tests/unit/lucatume/WPBrowser/Project/ThemeProjectTest.php
index 5a44e6719..8e4bb4894 100644
--- a/tests/unit/lucatume/WPBrowser/Project/ThemeProjectTest.php
+++ b/tests/unit/lucatume/WPBrowser/Project/ThemeProjectTest.php
@@ -7,7 +7,7 @@
use lucatume\WPBrowser\Project\ThemeProject;
use lucatume\WPBrowser\Tests\Traits\CliCommandTestingTools;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
diff --git a/tests/unit/lucatume/WPBrowser/Traits/UopzFunctionsTest.php b/tests/unit/lucatume/WPBrowser/Traits/UopzFunctionsTest.php
new file mode 100644
index 000000000..6c58766c0
--- /dev/null
+++ b/tests/unit/lucatume/WPBrowser/Traits/UopzFunctionsTest.php
@@ -0,0 +1,1253 @@
+setFunctionReturn('someTestFunction', 23);
+
+ $this->assertEquals(23, someTestFunction());
+ }
+
+ /**
+ * It should allow setting the return value of a namespaced function
+ *
+ * @test
+ */
+ public function should_allow_setting_the_return_value_of_a_namespaced_function(): void
+ {
+ $this->setFunctionReturn('lucatume\WPBrowser\Acme\Project\testFunction', 23);
+
+ $this->assertEquals(23, \lucatume\WPBrowser\Acme\Project\testFunction());
+ }
+
+ /**
+ * It should allow setting the return value of a function with a closure
+ *
+ * @test
+ */
+ public function should_allow_setting_the_return_value_of_a_function_with_a_closure(): void
+ {
+ $this->setFunctionReturn('someTestFunction', static function () {
+ return 23;
+ }, true);
+ $this->setFunctionReturn('lucatume\WPBrowser\Acme\Project\testFunction', static function () {
+ return 89;
+ }, true);
+
+ $this->assertEquals(23, someTestFunction());
+ $this->assertEquals(89, \lucatume\WPBrowser\Acme\Project\testFunction());
+ }
+
+ /**
+ * It should allow setting the return value of a function to a closure value
+ *
+ * @test
+ */
+ public function should_allow_setting_the_return_value_of_a_function_to_a_closure_value(): void
+ {
+ $closure1 = static function () {
+ return 23;
+ };
+ $this->setFunctionReturn('someTestFunction', $closure1);
+ $closure2 = static function () {
+ return 89;
+ };
+ $this->setFunctionReturn('lucatume\WPBrowser\Acme\Project\testFunction', $closure2);
+
+ $this->assertEquals($closure1, someTestFunction());
+ $this->assertEquals($closure2, \lucatume\WPBrowser\Acme\Project\testFunction());
+ }
+
+ /**
+ * It should handle functions with reference arguments
+ *
+ * @test
+ */
+ public function should_handle_functions_with_reference_arguments(): void
+ {
+ $this->setFunctionReturn('someReferenceFunction', null);
+
+ $input = [23, 89];
+ someReferenceFunction($input);
+ $this->assertEquals([23, 89], $input);
+
+ $this->setFunctionReturn('someReferenceFunction', function (array &$input) {
+ $input[] = 'hello';
+ }, true);
+
+ someReferenceFunction($input);
+
+ $this->assertEquals([23, 89, 'hello'], $input);
+
+ $this->setFunctionReturn('lucatume\WPBrowser\Acme\Project\someReferenceFunction', null);
+
+ $input = [23, 89];
+ \lucatume\WPBrowser\Acme\Project\someReferenceFunction($input);
+ $this->assertEquals([23, 89], $input);
+
+ $this->setFunctionReturn('lucatume\WPBrowser\Acme\Project\someReferenceFunction', function (array &$input) {
+ $input[] = 'hello';
+ }, true);
+
+ \lucatume\WPBrowser\Acme\Project\someReferenceFunction($input);
+
+ $this->assertEquals([23, 89, 'hello'], $input);
+ }
+
+ /**
+ * It should unset function return value between tests
+ *
+ * @test
+ */
+ public function should_unset_function_return_value_between_tests(): void
+ {
+ $this->assertEquals('test-test-test', someTestFunction());
+ $this->assertEquals('test-test-test', \lucatume\WPBrowser\Acme\Project\testFunction());
+ }
+
+ /**
+ * It should allow unsetting a function return value
+ *
+ * @test
+ */
+ public function should_allow_unsetting_a_function_return_value(): void
+ {
+ $this->setFunctionReturn('someTestFunction', 23);
+ $this->setFunctionReturn('lucatume\WPBrowser\Acme\Project\testFunction', 89);
+
+ $this->assertEquals(23, someTestFunction());
+ $this->assertEquals(89, \lucatume\WPBrowser\Acme\Project\testFunction());
+
+ $this->unsetFunctionReturn('someTestFunction');
+ $this->unsetFunctionReturn('lucatume\WPBrowser\Acme\Project\testFunction');
+
+ $this->assertEquals('test-test-test', someTestFunction());
+ $this->assertEquals('test-test-test', \lucatume\WPBrowser\Acme\Project\testFunction());
+ }
+
+ /**
+ * It should not throw when unsetting a non-set function return value
+ *
+ * @test
+ */
+ public function should_not_throw_when_unsetting_a_non_set_function_return_value(): void
+ {
+ $this->unsetFunctionReturn('someTestFunction');
+ $this->unsetFunctionReturn('Acme\Project\testFunction');
+ }
+
+ /**
+ * It should allow setting an instance method return value
+ *
+ * @test
+ */
+ public function should_allow_setting_an_instance_method_return_value(): void
+ {
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'getValueOne', 23);
+ $return23 = fn() => 23;
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'getValueTwo', $return23);
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'getValueThree', fn() => 89, true);
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'getValueOne', 23);
+ $return23 = fn() => 23;
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'getValueTwo', $return23);
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'getValueThree', fn() => 89, true);
+
+ $globalClass = new SomeGlobalClassOne();
+ $namespacedClass = new SomeNamespacedClassOne();
+
+ $this->assertEquals(23, $globalClass->getValueOne());
+ $this->assertEquals($return23, $globalClass->getValueTwo());
+ $this->assertEquals(89, $globalClass->getValueThree());
+ $this->assertEquals(23, $namespacedClass->getValueOne());
+ $this->assertEquals($return23, $namespacedClass->getValueTwo());
+ $this->assertEquals(89, $namespacedClass->getValueThree());
+ }
+
+ /**
+ * It should allow setting a static method return value
+ *
+ * @test
+ */
+ public function should_allow_setting_a_static_method_return_value(): void
+ {
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'getStaticValueOne', 23);
+ $return23 = fn() => 23;
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'getStaticValueTwo', $return23);
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'getStaticValueThree', fn() => 89, true);
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'getStaticValueOne', 23);
+ $return23 = fn() => 23;
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'getStaticValueTwo', $return23);
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'getStaticValueThree', fn() => 89, true);
+
+ $this->assertEquals(23, SomeGlobalClassOne::getStaticValueOne());
+ $this->assertEquals($return23, SomeGlobalClassOne::getStaticValueTwo());
+ $this->assertEquals(89, SomeGlobalClassOne::getStaticValueThree());
+ $this->assertEquals(23, SomeNamespacedClassOne::getStaticValueOne());
+ $this->assertEquals($return23, SomeNamespacedClassOne::getStaticValueTwo());
+ $this->assertEquals(89, SomeNamespacedClassOne::getStaticValueThree());
+ }
+
+ /**
+ * It should allow unsetting a set method return value
+ *
+ * @test
+ */
+ public function should_allow_unsetting_a_set_method_return_value(): void
+ {
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'getValueOne', 23);
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'getStaticValueOne', 23);
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'getValueOne', 23);
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'getStaticValueOne', 23);
+
+ $this->assertEquals(23, (new SomeGlobalClassOne())->getValueOne());
+ $this->assertEquals(23, SomeGlobalClassOne::getStaticValueOne());
+ $this->assertEquals(23, (new SomeNamespacedClassOne())->getValueOne());
+ $this->assertEquals(23, SomeNamespacedClassOne::getStaticValueOne());
+
+ $this->unsetMethodReturn(SomeGlobalClassOne::class, 'getValueOne');
+ $this->unsetMethodReturn(SomeGlobalClassOne::class, 'getStaticValueOne');
+ $this->unsetMethodReturn(SomeNamespacedClassOne::class, 'getValueOne');
+ $this->unsetMethodReturn(SomeNamespacedClassOne::class, 'getStaticValueOne');
+
+ $this->assertEquals('original-value-one', (new SomeGlobalClassOne())->getValueOne());
+ $this->assertEquals('original-static-value-one', SomeGlobalClassOne::getStaticValueOne());
+ $this->assertEquals('original-value-one', (new SomeNamespacedClassOne())->getValueOne());
+ $this->assertEquals('original-static-value-one', SomeNamespacedClassOne::getStaticValueOne());
+ }
+
+ /**
+ * It should not throw when unsetting a non-set method return value
+ *
+ * @test
+ */
+ public function should_not_throw_when_unsetting_a_non_set_method_return_value(): void
+ {
+ $this->unsetMethodReturn(SomeGlobalClassOne::class, 'getValueOne');
+ $this->unsetMethodReturn(SomeGlobalClassOne::class, 'getStaticValueOne');
+ $this->unsetMethodReturn(SomeNamespacedClassOne::class, 'getValueOne');
+ $this->unsetMethodReturn(SomeNamespacedClassOne::class, 'getStaticValueOne');
+ }
+
+ /**
+ * It should handle method with reference arguments
+ *
+ * @test
+ */
+ public function should_handle_method_with_reference_arguments(): void
+ {
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'modifyValueByReference', null);
+
+ $input = [23, 89];
+ $globalClass = new SomeGlobalClassOne();
+ $globalClass->modifyValueByReference($input);
+ $this->assertEquals([23, 89], $input);
+
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'modifyStaticValueByReference', null);
+
+ $input = [23, 89];
+ SomeGlobalClassOne::modifyStaticValueByReference($input);
+ $this->assertEquals([23, 89], $input);
+
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'modifyValueByReference', null);
+
+ $input = [23, 89];
+ $namespacedClass = new SomeNamespacedClassOne();
+ $namespacedClass->modifyValueByReference($input);
+ $this->assertEquals([23, 89], $input);
+
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'modifyStaticValueByReference', null);
+
+ $input = [23, 89];
+ SomeNamespacedClassOne::modifyStaticValueByReference($input);
+ $this->assertEquals([23, 89], $input);
+
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'modifyValueByReference', function (array &$input) {
+ $input[] = 'hello';
+ }, true);
+
+ $input = [23, 89];
+ $globalClass->modifyValueByReference($input);
+ $this->assertEquals([23, 89, 'hello'], $input);
+
+ $this->setMethodReturn(SomeGlobalClassOne::class, 'modifyStaticValueByReference', function (array &$input) {
+ $input[] = 'hello';
+ }, true);
+
+ $input = [23, 89];
+ SomeGlobalClassOne::modifyStaticValueByReference($input);
+ $this->assertEquals([23, 89, 'hello'], $input);
+
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'modifyValueByReference', function (array &$input) {
+ $input[] = 'hello';
+ }, true);
+
+ $input = [23, 89];
+ $namespacedClass->modifyValueByReference($input);
+ $this->assertEquals([23, 89, 'hello'], $input);
+
+ $this->setMethodReturn(SomeNamespacedClassOne::class, 'modifyStaticValueByReference', function (array &$input) {
+ $input[] = 'hello';
+ }, true);
+
+ $input = [23, 89];
+ SomeNamespacedClassOne::modifyStaticValueByReference($input);
+ $this->assertEquals([23, 89, 'hello'], $input);
+ }
+
+ /**
+ * It should allow setting a function hook
+ *
+ * @test
+ */
+ public function should_allow_setting_a_function_hook(): void
+ {
+ $headers = [];
+ $hook = function (string $header, bool $replace = true, int $response_code = 0) use (
+ &$headers
+ ): void {
+ $headers[] = [
+ 'header' => $header,
+ 'replace' => $replace,
+ 'response_code' => $response_code,
+ ];
+ };
+
+ $headers = [];
+ $this->setFunctionHook('header', $hook);
+
+ header('Location: http://example.com', true, 301);
+ header('X-Frame-Options: DENY', false, 200);
+
+ $this->assertEquals([
+ [
+ 'header' => 'Location: http://example.com',
+ 'replace' => true,
+ 'response_code' => 301,
+ ],
+ [
+ 'header' => 'X-Frame-Options: DENY',
+ 'replace' => false,
+ 'response_code' => 200,
+ ],
+ ], $headers);
+
+ $headers = [];
+ $this->setFunctionHook('someHeaderProxy', $hook);
+
+ someHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertEquals([
+ [
+ 'header' => 'X-REST-URL: http://example.com',
+ 'replace' => true,
+ 'response_code' => 0,
+ ],
+ [
+ 'header' => 'X-REST-URL: http://example.com',
+ 'replace' => true,
+ 'response_code' => 0,
+ ],
+ ], $headers);
+
+ $headers = [];
+ $this->setFunctionHook('lucatume\WPBrowser\Acme\Project\someHeaderProxy', $hook);
+
+ \lucatume\WPBrowser\Acme\Project\someHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertEquals([
+ [
+ 'header' => 'X-REST-URL: http://example.com',
+ 'replace' => true,
+ 'response_code' => 0,
+ ],
+ [
+ 'header' => 'X-REST-URL: http://example.com',
+ 'replace' => true,
+ 'response_code' => 0,
+ ],
+ ], $headers);
+ }
+
+ /**
+ * It should allow unsetting a function hook
+ *
+ * @test
+ */
+ public function should_allow_unsetting_a_function_hook(): void
+ {
+ $headers = [];
+ $hook = function (string $header, bool $replace = true, int $response_code = 0) use (
+ &$headers
+ ): void {
+ $headers[] = [
+ 'header' => $header,
+ 'replace' => $replace,
+ 'response_code' => $response_code,
+ ];
+ };
+
+ $this->setFunctionHook('header', $hook);
+ $this->setFunctionHook('someHeaderProxy', $hook);
+ $this->setFunctionHook('lucatume\WPBrowser\Acme\Project\someHeaderProxy', $hook);
+
+ header('Location: http://example.com', true, 301);
+ header('X-Frame-Options: DENY', false, 200);
+ someHeaderProxy('X-REST-URL: http://example.com');
+ \lucatume\WPBrowser\Acme\Project\someHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertCount(6, $headers);
+
+ $this->unsetFunctionHook('header');
+ $this->unsetFunctionHook('someHeaderProxy');
+ $this->unsetFunctionHook('lucatume\WPBrowser\Acme\Project\someHeaderProxy');
+ $headers = [];
+
+ header('Location: http://example.com', true, 301);
+ header('X-Frame-Options: DENY', false, 200);
+ someHeaderProxy('X-REST-URL: http://example.com');
+ \lucatume\WPBrowser\Acme\Project\someHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertCount(0, $headers);
+ }
+
+ /**
+ * It should not throw when unsetting a not set function hook
+ *
+ * @test
+ */
+ public function should_not_throw_when_unsetting_a_not_set_function_hook(): void
+ {
+ $this->unsetFunctionHook('header');
+ $this->unsetFunctionHook('someHeaderProxy');
+ $this->unsetFunctionHook('lucatume\WPBrowser\Acme\Project\someHeaderProxy');
+ }
+
+ /**
+ * It should allow setting a method hook
+ *
+ * @test
+ */
+ public function should_allow_setting_a_method_hook(): void
+ {
+ $headers = [];
+ $hook = function (string $header, bool $replace = true, int $response_code = 0) use (
+ &$headers
+ ): void {
+ $headers[] = [
+ 'header' => $header,
+ 'replace' => $replace,
+ 'response_code' => $response_code,
+ ];
+ };
+
+ $this->setMethodHook(SomeGlobalClassOne::class, 'someHeaderProxy', $hook);
+ $this->setMethodHook(SomeGlobalClassOne::class, 'someStaticHeaderProxy', $hook);
+ $this->setMethodHook(SomeNamespacedClassOne::class, 'someHeaderProxy', $hook);
+ $this->setMethodHook(SomeNamespacedClassOne::class, 'someStaticHeaderProxy', $hook);
+
+ $globalClass = new SomeGlobalClassOne();
+ $namespacedClass = new SomeNamespacedClassOne();
+
+ $headers = [];
+ $globalClass->someHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertEquals([
+ [
+ 'header' => 'X-REST-URL: http://example.com',
+ 'replace' => true,
+ 'response_code' => 0,
+ ]
+ ], $headers);
+
+ $headers = [];
+ SomeGlobalClassOne::someStaticHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertEquals([
+ [
+ 'header' => 'X-REST-URL: http://example.com',
+ 'replace' => true,
+ 'response_code' => 0,
+ ]
+ ], $headers);
+
+ $headers = [];
+ $namespacedClass->someHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertEquals([
+ [
+ 'header' => 'X-REST-URL: http://example.com',
+ 'replace' => true,
+ 'response_code' => 0,
+ ]
+ ], $headers);
+
+ $headers = [];
+ SomeNamespacedClassOne::someStaticHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertEquals([
+ [
+ 'header' => 'X-REST-URL: http://example.com',
+ 'replace' => true,
+ 'response_code' => 0,
+ ]
+ ], $headers);
+
+ $headers = [];
+
+ $this->unsetMethodHook(SomeGlobalClassOne::class, 'someHeaderProxy');
+ $this->unsetMethodHook(SomeGlobalClassOne::class, 'someStaticHeaderProxy');
+ $this->unsetMethodHook(SomeNamespacedClassOne::class, 'someHeaderProxy');
+ $this->unsetMethodHook(SomeNamespacedClassOne::class, 'someStaticHeaderProxy');
+
+ $globalClass->someHeaderProxy('X-REST-URL: http://example.com');
+ SomeGlobalClassOne::someStaticHeaderProxy('X-REST-URL: http://example.com');
+ $namespacedClass->someHeaderProxy('X-REST-URL: http://example.com');
+ SomeNamespacedClassOne::someStaticHeaderProxy('X-REST-URL: http://example.com');
+
+ $this->assertCount(0, $headers);
+ }
+
+ /**
+ * It should not throw if unsetting a not set method hook
+ *
+ * @test
+ */
+ public function should_not_throw_if_unsetting_a_not_set_method_hook(): void
+ {
+ $this->unsetMethodHook(SomeGlobalClassOne::class, 'someHeaderProxy');
+ $this->unsetMethodHook(SomeGlobalClassOne::class, 'someStaticHeaderProxy');
+ $this->unsetMethodHook(SomeNamespacedClassOne::class, 'someHeaderProxy');
+ $this->unsetMethodHook(SomeNamespacedClassOne::class, 'someStaticHeaderProxy');
+ }
+
+ /**
+ * It should allow setting a constant
+ *
+ * @test
+ */
+ public function should_allow_setting_a_constant(): void
+ {
+ $this->setConstant('EXISTING_CONSTANT', 23);
+ $this->setConstant('NOT_EXISTING_CONSTANT', 89);
+ $this->setConstant('lucatume\WPBrowser\Acme\Project\EXISTING_CONSTANT', 89);
+ $this->setConstant('lucatume\WPBrowser\Acme\Project\NOT_EXISTING_CONSTANT', 23);
+
+ $this->assertEquals(23, EXISTING_CONSTANT);
+ $this->assertEquals(89, \NOT_EXISTING_CONSTANT);
+ $this->assertEquals(89, \lucatume\WPBrowser\Acme\Project\EXISTING_CONSTANT);
+ $this->assertEquals(23, NOT_EXISTING_CONSTANT);
+
+ $this->unsetConstant('EXISTING_CONSTANT');
+ $this->unsetConstant('NOT_EXISTING_CONSTANT');
+ $this->unsetConstant('lucatume\WPBrowser\Acme\Project\EXISTING_CONSTANT');
+ $this->unsetConstant('lucatume\WPBrowser\Acme\Project\NOT_EXISTING_CONSTANT');
+
+ $this->assertEquals('test-constant', EXISTING_CONSTANT);
+ $this->assertFalse(defined('NOT_EXISTING_CONSTANT'));
+ $this->assertEquals('test-constant', \lucatume\WPBrowser\Acme\Project\EXISTING_CONSTANT);
+ $this->assertFalse(defined('\lucatume\WPBrowser\Acme\Project\NOT_EXISTING_CONSTANT'));
+ }
+
+ /**
+ * It should allow setting a class constant
+ *
+ * @test
+ */
+ public function should_allow_setting_a_class_constant(): void
+ {
+ $this->setClassConstant('SomeGlobalClassOne', 'EXISTING_CONSTANT', 23);
+ $this->setClassConstant('SomeGlobalClassOne', 'NOT_EXISTING_CONSTANT', 89);
+ $this->setClassConstant('lucautme\WPBrowser\Acme\Project\SomeNamespacedClassOne', 'EXISTING_CONSTANT', 89);
+ $this->setClassConstant('lucautme\WPBrowser\Acme\Project\SomeNamespacedClassOne', 'NOT_EXISTING_CONSTANT', 23);
+
+ $this->assertEquals(23, SomeGlobalClassOne::EXISTING_CONSTANT);
+ $this->assertEquals(89, SomeGlobalClassOne::NOT_EXISTING_CONSTANT);
+ $this->assertEquals(89, SomeNamespacedClassOne::EXISTING_CONSTANT);
+ $this->assertEquals(23, SomeNamespacedClassOne::NOT_EXISTING_CONSTANT);
+
+ $this->unsetClassConstant('SomeGlobalClassOne', 'EXISTING_CONSTANT');
+ $this->unsetClassConstant('SomeGlobalClassOne', 'NOT_EXISTING_CONSTANT');
+ $this->unsetClassConstant('lucautme\WPBrowser\Acme\Project\SomeNamespacedClassOne', 'EXISTING_CONSTANT');
+ $this->unsetClassConstant('lucautme\WPBrowser\Acme\Project\SomeNamespacedClassOne', 'NOT_EXISTING_CONSTANT');
+
+ $this->assertEquals('test-constant', SomeGlobalClassOne::EXISTING_CONSTANT);
+ $this->assertFalse(defined('SomeGlobalClassOne::NOT_EXISTING_CONSTANT'));
+ $this->assertEquals(
+ 'test-constant',
+ SomeNamespacedClassOne::EXISTING_CONSTANT
+ );
+ $this->assertFalse(defined('\lucautme\WPBrowser\Acme\Project\SomeNamespacedClassOne::NOT_EXISTING_CONSTANT'));
+ }
+
+ /**
+ * It should allow setting a class mock to instance
+ *
+ * @test
+ */
+ public function should_allow_setting_a_class_mock_to_instance(): void
+ {
+ $mockSomeGlobalClassOne = new class extends SomeGlobalClassOne {
+ public function getValueOne(): string
+ {
+ return 'mocked-value-one';
+ }
+ };
+ $this->setClassMock(SomeGlobalClassOne::class, $mockSomeGlobalClassOne);
+
+ $mockSomeGlobalClassOneInstanceOne = new SomeGlobalClassOne();
+ $mockSomeGlobalClassOneInstanceTwo = new SomeGlobalClassOne();
+
+ $this->assertSame($mockSomeGlobalClassOne, $mockSomeGlobalClassOneInstanceOne);
+ $this->assertSame($mockSomeGlobalClassOne, $mockSomeGlobalClassOneInstanceTwo);
+ $this->assertEquals('mocked-value-one', $mockSomeGlobalClassOneInstanceOne->getValueOne());
+ $this->assertEquals('mocked-value-one', $mockSomeGlobalClassOneInstanceTwo->getValueOne());
+
+ $mockSomeNamespacedClassOne = new class extends SomeNamespacedClassOne {
+ public function getValueOne(): string
+ {
+ return 'mocked-value-one';
+ }
+ };
+ $this->setClassMock(
+ SomeNamespacedClassOne::class,
+ $mockSomeNamespacedClassOne
+ );
+
+ $mockSomeNamespacedClassOneInstanceOne = new SomeNamespacedClassOne();
+ $mockSomeNamespacedClassOneInstanceTwo = new SomeNamespacedClassOne();
+ $this->assertEquals('mocked-value-one', $mockSomeNamespacedClassOneInstanceOne->getValueOne());
+ $this->assertEquals('mocked-value-one', $mockSomeNamespacedClassOneInstanceTwo->getValueOne());
+
+ $this->unsetClassMock(SomeGlobalClassOne::class);
+ $this->unsetClassMock(SomeNamespacedClassOne::class);
+
+ $this->assertNotSame($mockSomeGlobalClassOne, new SomeGlobalClassOne());
+ $this->assertNotSame($mockSomeNamespacedClassOne, new SomeNamespacedClassOne());
+ }
+
+ /**
+ * It should allow setting a class mock to a class
+ *
+ * @test
+ */
+ public function should_allow_setting_a_class_mock_to_a_class(): void
+ {
+ $this->setClassMock(SomeGlobalClassOne::class, SomeGlobalClassTwo::class);
+
+ $mockSomeGlobalClassOneInstanceOne = new SomeGlobalClassOne();
+ $mockSomeGlobalClassOneInstanceTwo = new SomeGlobalClassOne();
+
+ $this->assertInstanceOf(SomeGlobalClassTwo::class, $mockSomeGlobalClassOneInstanceOne);
+ $this->assertInstanceOf(SomeGlobalClassTwo::class, $mockSomeGlobalClassOneInstanceTwo);
+ $this->assertNotSame($mockSomeGlobalClassOneInstanceOne, $mockSomeGlobalClassOneInstanceTwo);
+ $this->assertEquals('another-value', $mockSomeGlobalClassOneInstanceOne->getValueOne());
+ $this->assertEquals('another-value', $mockSomeGlobalClassOneInstanceTwo->getValueOne());
+
+ $this->setClassMock(SomeNamespacedClassOne::class, SomeNamespacedClassTwo::class);
+
+ $mockSomeNamespacedClassOneInstanceOne = new SomeNamespacedClassOne();
+ $mockSomeNamespacedClassOneInstanceTwo = new SomeNamespacedClassOne();
+
+ $this->assertInstanceOf(SomeNamespacedClassTwo::class, $mockSomeNamespacedClassOneInstanceOne);
+ $this->assertInstanceOf(SomeNamespacedClassTwo::class, $mockSomeNamespacedClassOneInstanceTwo);
+ $this->assertNotSame($mockSomeNamespacedClassOneInstanceOne, $mockSomeNamespacedClassOneInstanceTwo);
+ $this->assertEquals('another-value', $mockSomeNamespacedClassOneInstanceOne->getValueOne());
+ $this->assertEquals('another-value', $mockSomeNamespacedClassOneInstanceTwo->getValueOne());
+
+ $this->unsetClassMock(SomeGlobalClassOne::class);
+ $this->unsetClassMock(SomeNamespacedClassOne::class);
+
+ $this->assertInstanceOf(SomeGlobalClassOne::class, new SomeGlobalClassOne());
+ $this->assertInstanceOf(SomeNamespacedClassOne::class, new SomeNamespacedClassOne());
+ }
+
+ /**
+ * It should not throw if trying to unset a not set class mock
+ *
+ * @test
+ */
+ public function should_not_throw_if_trying_to_unset_a_not_set_class_mock(): void
+ {
+ $this->unsetClassMock(SomeGlobalClassOne::class);
+ $this->unsetClassMock(SomeNamespacedClassOne::class);
+ }
+
+ /**
+ * It should allow unsetting a class final attribute
+ *
+ * @test
+ */
+ public function should_allow_unsetting_a_class_final_attribute(): void
+ {
+ $this->unsetClassFinalAttribute(SomeGlobalFinalClass::class);
+ $this->unsetClassFinalAttribute(SomeNamespacedFinalClass::class);
+
+ $globalExtension = new class extends SomeGlobalFinalClass {
+ public function someMethod(): int
+ {
+ return 89;
+ }
+ };
+ $this->assertEquals(89, $globalExtension->someMethod());
+
+ $namespacedExtension = new class extends SomeNamespacedFinalClass {
+ public function someMethod(): int
+ {
+ return 89;
+ }
+ };
+ $this->assertEquals(89, $namespacedExtension->someMethod());
+
+ $this->resetClassFinalAttribute(SomeGlobalFinalClass::class);
+ $this->resetClassFinalAttribute(SomeNamespacedFinalClass::class);
+
+ $this->assertTrue((new ReflectionClass(SomeGlobalFinalClass::class))->isFinal());
+ $this->assertTrue((new ReflectionClass(SomeNamespacedFinalClass::class))->isFinal());
+ }
+
+ /**
+ * It should not throw if trying to reset a not set class final attribute
+ *
+ * @test
+ */
+ public function should_not_throw_if_trying_to_reset_a_not_set_class_final_attribute(): void
+ {
+ $this->resetClassFinalAttribute(SomeGlobalFinalClass::class);
+ $this->resetClassFinalAttribute(SomeNamespacedFinalClass::class);
+ }
+
+ /**
+ * It should allow unsetting a class method final attribute
+ *
+ * @test
+ */
+ public function should_allow_unsetting_a_class_method_final_attribute(): void
+ {
+ $this->unsetMethodFinalAttribute(SomeGlobalClassWithFinalMethods::class, 'someFinalMethod');
+ $this->unsetMethodFinalAttribute(SomeGlobalClassWithFinalMethods::class, 'someStaticFinalMethod');
+ $this->unsetMethodFinalAttribute(SomeNamespacedClassWithFinalMethods::class, 'someFinalMethod');
+ $this->unsetMethodFinalAttribute(SomeNamespacedClassWithFinalMethods::class, 'someStaticFinalMethod');
+
+ $globalExtension = new class extends SomeGlobalClassWithFinalMethods {
+ public function someFinalMethod(): int
+ {
+ return 123;
+ }
+
+ public static function someStaticFinalMethod(): int
+ {
+ return 189;
+ }
+ };
+
+ $this->assertEquals(123, $globalExtension->someFinalMethod());
+ $this->assertEquals(189, $globalExtension::someStaticFinalMethod());
+
+ $namespacedExtension = new class extends SomeNamespacedClassWithFinalMethods {
+ public function someFinalMethod(): int
+ {
+ return 91;
+ }
+
+ public static function someStaticFinalMethod(): int
+ {
+ return 66;
+ }
+ };
+
+ $this->assertEquals(91, $namespacedExtension->someFinalMethod());
+ $this->assertEquals(66, $namespacedExtension::someStaticFinalMethod());
+
+ $this->resetMethodFinalAttribute(SomeGlobalClassWithFinalMethods::class, 'someFinalMethod');
+ $this->resetMethodFinalAttribute(SomeGlobalClassWithFinalMethods::class, 'someStaticFinalMethod');
+ $this->resetMethodFinalAttribute(SomeNamespacedClassWithFinalMethods::class, 'someFinalMethod');
+ $this->resetMethodFinalAttribute(SomeNamespacedClassWithFinalMethods::class, 'someStaticFinalMethod');
+
+ $this->assertTrue(
+ (new ReflectionMethod(SomeGlobalClassWithFinalMethods::class, 'someFinalMethod'))->isFinal()
+ );
+ $this->assertTrue(
+ (new ReflectionMethod(SomeGlobalClassWithFinalMethods::class, 'someStaticFinalMethod'))->isFinal()
+ );
+ $this->assertTrue(
+ (new ReflectionMethod(SomeNamespacedClassWithFinalMethods::class, 'someFinalMethod'))->isFinal()
+ );
+ $this->assertTrue(
+ (new ReflectionMethod(SomeNamespacedClassWithFinalMethods::class, 'someStaticFinalMethod'))->isFinal()
+ );
+ }
+
+ /**
+ * It should not throw if trying to reset a not unset method final attribute
+ *
+ * @test
+ */
+ public function should_not_throw_if_trying_to_reset_a_not_unset_method_final_attribute(): void
+ {
+ $this->resetMethodFinalAttribute(SomeGlobalClassWithFinalMethods::class, 'someFinalMethod');
+ $this->resetMethodFinalAttribute(SomeGlobalClassWithFinalMethods::class, 'someStaticFinalMethod');
+ $this->resetMethodFinalAttribute(SomeNamespacedClassWithFinalMethods::class, 'someFinalMethod');
+ $this->resetMethodFinalAttribute(SomeNamespacedClassWithFinalMethods::class, 'someStaticFinalMethod');
+ }
+
+ /**
+ * It should allow adding class methods
+ *
+ * @test
+ */
+ public function should_allow_adding_class_methods(): void
+ {
+ $this->addClassMethod(SomeGlobalClassWithoutMethods::class, 'instanceMethod', function (): int {
+ return $this->number;
+ });
+ $this->addClassMethod(SomeGlobalClassWithoutMethods::class, 'staticMethod', function (): string {
+ return self::$name;
+ }, true);
+
+ $this->assertEquals(23, (new SomeGlobalClassWithoutMethods())->instanceMethod());
+ $this->assertEquals('Luca', SomeGlobalClassWithoutMethods::staticMethod());
+
+ $this->removeClassMethod(SomeGlobalClassWithoutMethods::class, 'instanceMethod');
+ $this->removeClassMethod(SomeGlobalClassWithoutMethods::class, 'staticMethod');
+
+ $this->assertFalse(method_exists(SomeGlobalClassWithoutMethods::class, 'instanceMethod'));
+ $this->assertFalse(method_exists(SomeGlobalClassWithoutMethods::class, 'staticMethod'));
+
+ $this->addClassMethod(SomeNamespacedClassWithoutMethods::class, 'instanceMethod', function (): int {
+ return $this->number;
+ });
+ $this->addClassMethod(SomeNamespacedClassWithoutMethods::class, 'staticMethod', function (): string {
+ return self::$name;
+ }, true);
+
+ $this->assertEquals(23, (new SomeNamespacedClassWithoutMethods())->instanceMethod());
+ $this->assertEquals('Luca', SomeNamespacedClassWithoutMethods::staticMethod());
+
+ $this->removeClassMethod(SomeNamespacedClassWithoutMethods::class, 'instanceMethod');
+ $this->removeClassMethod(SomeNamespacedClassWithoutMethods::class, 'staticMethod');
+
+ $this->assertFalse(method_exists(SomeNamespacedClassWithoutMethods::class, 'instanceMethod'));
+ $this->assertFalse(method_exists(SomeNamespacedClassWithoutMethods::class, 'staticMethod'));
+ }
+
+ /**
+ * It should not throw if trying to remove not added class methods
+ *
+ * @test
+ */
+ public function should_not_throw_if_trying_to_remove_not_added_class_methods(): void
+ {
+ $this->removeClassMethod(SomeGlobalClassWithoutMethods::class, 'someNonExistingInstanceMethod');
+ $this->removeClassMethod(SomeGlobalClassWithoutMethods::class, 'someNonExistingStaticMethod');
+ $this->removeClassMethod(SomeNamespacedClassWithoutMethods::class, 'someNonExistingInstanceMethod');
+ $this->removeClassMethod(SomeNamespacedClassWithoutMethods::class, 'someNonExistingStaticMethod');
+ }
+
+ /**
+ * It should allow setting object properties
+ *
+ * @test
+ */
+ public function should_allow_setting_object_properties(): void
+ {
+ $globalClassInstance = new SomeGlobalClassWithoutMethods();
+ $this->setObjectProperty($globalClassInstance, 'number', 89);
+ $this->setObjectProperty(SomeGlobalClassWithoutMethods::class, 'name', 'Bob');
+
+ $this->assertEquals(89, $this->getObjectProperty($globalClassInstance, 'number'));
+ $this->assertEquals('Bob', $this->getObjectProperty(SomeGlobalClassWithoutMethods::class, 'name'));
+
+ $this->resetObjectProperty($globalClassInstance, 'number');
+ $this->resetObjectProperty(SomeGlobalClassWithoutMethods::class, 'name');
+ $this->resetObjectProperty($globalClassInstance, 'someNonExistingInstanceProperty');
+ $this->resetObjectProperty(SomeGlobalClassWithoutMethods::class, 'someNonExistingStaticProperty');
+
+ $this->assertEquals(23, $this->getObjectProperty($globalClassInstance, 'number'));
+ $this->assertEquals('Luca', $this->getObjectProperty(SomeGlobalClassWithoutMethods::class, 'name'));
+
+ $namespacedClassInstance = new SomeNamespacedClassWithoutMethods();
+ $this->setObjectProperty($namespacedClassInstance, 'number', 89);
+ $this->setObjectProperty(SomeNamespacedClassWithoutMethods::class, 'name', 'Bob');
+
+ $this->assertEquals(89, $this->getObjectProperty($namespacedClassInstance, 'number'));
+ $this->assertEquals('Bob', $this->getObjectProperty(SomeNamespacedClassWithoutMethods::class, 'name'));
+
+ $this->resetObjectProperty($namespacedClassInstance, 'number');
+ $this->resetObjectProperty(SomeNamespacedClassWithoutMethods::class, 'name');
+ $this->resetObjectProperty($namespacedClassInstance, 'someNonExistingInstanceProperty');
+ $this->resetObjectProperty(SomeNamespacedClassWithoutMethods::class, 'someNonExistingStaticProperty');
+
+ $this->assertEquals(23, $this->getObjectProperty($namespacedClassInstance, 'number'));
+ $this->assertEquals('Luca', $this->getObjectProperty(SomeNamespacedClassWithoutMethods::class, 'name'));
+ }
+
+ /**
+ * It should not throw if trying to reset not set object properties
+ *
+ * @test
+ */
+ public function should_not_throw_if_trying_to_reset_not_set_object_properties(): void
+ {
+ $globalClassInstance = new SomeGlobalClassWithoutMethods();
+ $this->resetObjectProperty($globalClassInstance, 'someNonExistingInstanceProperty');
+ $this->resetObjectProperty(SomeGlobalClassWithoutMethods::class, 'someNonExistingStaticProperty');
+
+ $namespacedClassInstance = new SomeNamespacedClassWithoutMethods();
+ $this->resetObjectProperty($namespacedClassInstance, 'someNonExistingInstanceProperty');
+ $this->resetObjectProperty(SomeNamespacedClassWithoutMethods::class, 'someNonExistingStaticProperty');
+ }
+
+ /**
+ * It should allow setting a method static variable
+ *
+ * @test
+ */
+ public function should_allow_setting_a_method_static_variable(): void
+ {
+ $someGlobalClassWithStaticVariablesInstance = new SomeGlobalClassWithStaticVariables();
+
+ $this->setMethodStaticVariables(
+ SomeGlobalClassWithStaticVariables::class,
+ 'theCounter',
+ array_merge(
+ $this->getMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theCounter'),
+ ['counter' => 23]
+ )
+ );
+ $this->setMethodStaticVariables(
+ SomeGlobalClassWithStaticVariables::class,
+ 'theStaticCounter',
+ array_merge(
+ $this->getMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theStaticCounter'),
+ ['counter' => 89]
+ )
+ );
+
+ $this->assertEquals(23, $someGlobalClassWithStaticVariablesInstance->theCounter());
+ $this->assertEquals(89, SomeGlobalClassWithStaticVariables::theStaticCounter());
+
+ $this->resetMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theCounter');
+ $this->resetMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theStaticCounter');
+
+ $this->assertEquals(
+ 0,
+ $this->getMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theCounter')['counter']
+ );
+ $this->assertEquals(
+ 0,
+ $this->getMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theStaticCounter')['counter']
+ );
+
+ $namespacedClassWithStaticVariablesInstance = new NamespacedClassWithStaticVariables();
+
+ $this->setMethodStaticVariables(
+ NamespacedClassWithStaticVariables::class,
+ 'theCounter',
+ array_merge(
+ $this->getMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theCounter'),
+ ['counter' => 23]
+ )
+ );
+ $this->setMethodStaticVariables(
+ NamespacedClassWithStaticVariables::class,
+ 'theStaticCounter',
+ array_merge(
+ $this->getMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theStaticCounter'),
+ ['counter' => 89]
+ )
+ );
+
+ $this->assertEquals(23, $namespacedClassWithStaticVariablesInstance->theCounter());
+ $this->assertEquals(89, NamespacedClassWithStaticVariables::theStaticCounter());
+
+ $this->resetMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theCounter');
+ $this->resetMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theStaticCounter');
+
+ $this->assertEquals(
+ 0,
+ $this->getMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theCounter')['counter']
+ );
+ $this->assertEquals(
+ 0,
+ $this->getMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theStaticCounter')['counter']
+ );
+
+
+ $this->setMethodStaticVariables(
+ SomeGlobalClassWithStaticVariables::class,
+ 'theCounter',
+ ['counter' => 23]
+ );
+ $this->setMethodStaticVariables(
+ SomeGlobalClassWithStaticVariables::class,
+ 'theStaticCounter',
+ ['counter' => 89]
+ );
+ $this->setMethodStaticVariables(
+ NamespacedClassWithStaticVariables::class,
+ 'theCounter',
+ ['counter' => 89, 'step' => 12]
+ );
+ $this->setMethodStaticVariables(
+ NamespacedClassWithStaticVariables::class,
+ 'theStaticCounter',
+ ['counter' => 14, 'step' => 13]
+ );
+ }
+
+ /**
+ * It should reset methods static variables between tests
+ *
+ * @test
+ */
+ public function should_reset_methods_static_variables_between_tests(): void
+ {
+ $this->assertEquals(
+ ['counter' => 0, 'step' => 2],
+ $this->getMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theCounter')
+ );
+ $this->assertEquals(
+ ['counter' => 0, 'step' => 2],
+ $this->getMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theStaticCounter')
+ );
+ $this->assertEquals(
+ ['counter' => 0],
+ $this->getMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theCounter')
+ );
+ $this->assertEquals(
+ ['counter' => 0],
+ $this->getMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theStaticCounter')
+ );
+ }
+
+ /**
+ * It should not throw if trying to reset a not set method static variable
+ *
+ * @test
+ */
+ public function should_not_throw_if_trying_to_reset_a_not_set_method_static_variable(): void
+ {
+ $this->resetMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theCounter');
+ $this->resetMethodStaticVariables(SomeGlobalClassWithStaticVariables::class, 'theStaticCounter');
+ $this->resetMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theCounter');
+ $this->resetMethodStaticVariables(NamespacedClassWithStaticVariables::class, 'theStaticCounter');
+ }
+
+ /**
+ * It should allow setting a function static variables
+ *
+ * @test
+ */
+ public function should_allow_setting_a_function_static_variables(): void
+ {
+ $this->assertEquals(
+ ['counter' => 0, 'step' => 2],
+ $this->getFunctionStaticVariables('withStaticVariable')
+ );
+
+ $this->setFunctionStaticVariables(
+ 'withStaticVariable',
+ array_merge(
+ $this->getFunctionStaticVariables('withStaticVariable'),
+ ['counter' => 23]
+ )
+ );
+
+ $this->assertEquals(23, withStaticVariable());
+
+ $this->resetFunctionStaticVariables('withStaticVariable');
+
+ $this->assertEquals(0, withStaticVariable());
+ $this->assertEquals(['counter' => 2, 'step' => 2], $this->getFunctionStaticVariables('withStaticVariable'));
+
+ $this->assertEquals(
+ ['counter' => 0, 'step' => 2],
+ $this->getFunctionStaticVariables('lucatume\WPBrowser\Acme\Project\withStaticVariable')
+ );
+
+ $this->setFunctionStaticVariables(
+ 'lucatume\WPBrowser\Acme\Project\withStaticVariable',
+ array_merge(
+ $this->getFunctionStaticVariables('lucatume\WPBrowser\Acme\Project\withStaticVariable'),
+ ['counter' => 23]
+ )
+ );
+
+ $this->assertEquals(23, \lucatume\WPBrowser\Acme\Project\withStaticVariable());
+
+ $this->resetFunctionStaticVariables('lucatume\WPBrowser\Acme\Project\withStaticVariable');
+
+ $this->assertEquals(0, \lucatume\WPBrowser\Acme\Project\withStaticVariable());
+ $this->assertEquals(
+ ['counter' => 2, 'step' => 2],
+ $this->getFunctionStaticVariables('lucatume\WPBrowser\Acme\Project\withStaticVariable')
+ );
+
+ $this->setFunctionStaticVariables(
+ 'withStaticVariable',
+ ['counter' => 89, 'step' => 3]
+ );
+
+ $this->setFunctionStaticVariables(
+ 'lucatume\WPBrowser\Acme\Project\withStaticVariable',
+ ['counter' => 89, 'step' => 3]
+ );
+ }
+
+ /**
+ * It should reset function static variables between tests
+ *
+ * @test
+ */
+ public function should_reset_function_static_variables_between_tests(): void
+ {
+ $this->assertEquals(2, withStaticVariable());
+ $this->assertEquals(2, \lucatume\WPBrowser\Acme\Project\withStaticVariable());
+ }
+
+ /**
+ * It should not throw if trying to reset not set function static variable
+ *
+ * @test
+ */
+ public
+ function should_not_throw_if_trying_to_reset_not_set_function_static_variable(): void
+ {
+ $this->resetFunctionStaticVariables('withStaticVariable');
+ $this->resetFunctionStaticVariables('lucatume\WPBrowser\Acme\Project\withStaticVariable');
+ }
+
+ /**
+ * It should allow adding and removing functions
+ *
+ * @test
+ */
+ public function should_allow_adding_and_removing_functions(): void
+ {
+ $this->assertFalse(function_exists('addTwentyThree'));
+
+ $this->addFunction('addTwentyThree', function (int $number) {
+ return $number + 23;
+ });
+
+ $this->assertTrue(function_exists('addTwentyThree'));
+ $this->assertEquals(89, addTwentyThree(66));
+
+ $this->removeFunction('addTwentyThree');
+
+ $this->assertFalse(function_exists('addTwentyThree'));
+
+ $this->addFunction('addTwentyThree', function (int $number) {
+ return $number + 23;
+ });
+
+ $this->assertFalse(function_exists('Acme\Project\addTwentyThree'));
+
+ $this->addFunction('Acme\Project\addTwentyThree', function (int $number) {
+ return $number + 23;
+ });
+
+ $this->assertTrue(function_exists('Acme\Project\addTwentyThree'));
+ $this->assertEquals(89, \Acme\Project\addTwentyThree(66));
+
+ $this->removeFunction('Acme\Project\addTwentyThree');
+
+ $this->assertFalse(function_exists('Acme\Project\addTwentyThree'));
+
+ $this->addFunction('Acme\Project\addTwentyThree', function (int $number) {
+ return $number + 23;
+ });
+ }
+
+ /**
+ * It should remove added functions between tests
+ *
+ * @test
+ */
+ public function should_remove_added_functions_between_tests(): void
+ {
+ $this->assertFalse(function_exists('addTwentyThree'));
+ $this->assertFalse(function_exists('Acme\Project\addTwentyThree'));
+ }
+
+ /**
+ * It should not throw if trying to remove a not added function
+ *
+ * @test
+ */
+ public function should_not_throw_if_trying_to_remove_a_not_added_function(): void
+ {
+ $this->removeFunction('addTwentyThree');
+ $this->removeFunction('Acme\Project\addTwentyThree');
+ }
+
+ /**
+ * It should allow preventing exit
+ *
+ * @test
+ */
+ public function should_allow_preventing_exit(): void
+ {
+ $this->preventExit();
+
+ $this->assertEquals(1,ini_get('uopz.exit'));
+ ob_start();
+ echo "Print this and die\n";
+ die();
+
+ $this->assertEquals("Print this and die\n",ob_get_clean());
+ }
+
+ /**
+ * It should not throw if trying to allow exit when exit not prevented
+ *
+ * @test
+ */
+ public function should_not_throw_if_trying_to_allow_exit_when_exit_not_prevented(): void
+ {
+ $this->allowExit();
+ }
+
+ /**
+ * It should restore exit between tests
+ *
+ * @test
+ */
+ public function should_restore_exit_between_tests(): void
+ {
+ $this->assertEquals(1,ini_get('uopz.exit'));
+ }
+}
diff --git a/tests/unit/lucatume/WPBrowser/Utils/ChromedriverInstallerTest.php b/tests/unit/lucatume/WPBrowser/Utils/ChromedriverInstallerTest.php
index e3e687608..c8c4c2a78 100644
--- a/tests/unit/lucatume/WPBrowser/Utils/ChromedriverInstallerTest.php
+++ b/tests/unit/lucatume/WPBrowser/Utils/ChromedriverInstallerTest.php
@@ -6,7 +6,7 @@
use lucatume\WPBrowser\Exceptions\InvalidArgumentException;
use lucatume\WPBrowser\Exceptions\RuntimeException;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
class ChromedriverInstallerTest extends \Codeception\Test\Unit
{
@@ -20,7 +20,7 @@ class ChromedriverInstallerTest extends \Codeception\Test\Unit
*/
public function should_throw_if_detected_platform_is_not_supported(): void
{
- $this->uopzSetFunctionReturn('php_uname', 'Lorem');
+ $this->setFunctionReturn('php_uname', 'Lorem');
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(ChromedriverInstaller::ERR_DETECT_PLATFORM);
@@ -48,7 +48,7 @@ public function should_throw_if_specified_platform_is_not_supported(): void
*/
public function should_throw_if_specified_binary_cannot_be_found(): void
{
- $this->uopzSetFunctionReturn('is_file', fn(string $file) => !str_contains($file, 'chrome'), true);
+ $this->setFunctionReturn('is_file', fn(string $file) => !str_contains($file, 'chrome'), true);
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(ChromedriverInstaller::ERR_INVALID_BINARY);
@@ -80,8 +80,8 @@ public function should_throw_if_binary_cannot_be_found_in_default_paths_for_plat
string $binNamePattern
): void {
$isNotAnExecutableFile = fn(string $file) => !str_contains($file, $binNamePattern);
- $this->uopzSetFunctionReturn('is_file', $isNotAnExecutableFile, true);
- $this->uopzSetFunctionReturn('is_executable', $isNotAnExecutableFile, true);
+ $this->setFunctionReturn('is_file', $isNotAnExecutableFile, true);
+ $this->setFunctionReturn('is_executable', $isNotAnExecutableFile, true);
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(ChromedriverInstaller::ERR_INVALID_BINARY);
@@ -95,7 +95,7 @@ public function should_throw_if_binary_cannot_be_found_in_default_paths_for_plat
*/
public function should_throw_if_binary_cannot_be_executed(): void
{
- $this->uopzSetFunctionReturn('is_executable', function (string $file): bool {
+ $this->setFunctionReturn('is_executable', function (string $file): bool {
return !str_contains($file, 'chrome') && is_executable($file);
}, true);
@@ -112,7 +112,7 @@ public function should_throw_if_binary_cannot_be_executed(): void
*/
public function should_throw_if_specified_binary_is_not_valid(): void
{
- $this->uopzSetFunctionReturn('is_executable', function (string $file): bool {
+ $this->setFunctionReturn('is_executable', function (string $file): bool {
return !str_contains($file, 'Chromium') && is_executable($file);
}, true);
@@ -150,7 +150,7 @@ public function should_throw_if_version_from_binary_is_not_a_string(): void
*/
public function should_throw_if_version_from_binary_has_not_correct_format(): void
{
- $this->uopzSetFunctionReturn('exec', 'Could not start Google Chrome.');
+ $this->setFunctionReturn('exec', 'Could not start Google Chrome.');
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(ChromedriverInstaller::ERR_INVALID_VERSION_FORMAT);
@@ -206,7 +206,7 @@ public function should_throw_if_trying_to_install_to_non_existing_directory(): v
*/
public function should_throw_if_it_cannot_get_milestone_downloads(): void
{
- $this->uopzSetFunctionReturn('file_get_contents', function (string $file): string|false {
+ $this->setFunctionReturn('file_get_contents', function (string $file): string|false {
return str_contains($file, 'chrome-for-testing') ? false : file_get_contents($file);
}, true);
@@ -226,7 +226,7 @@ public function should_throw_if_it_cannot_get_milestone_downloads(): void
*/
public function should_throw_if_response_is_not_valid_json(): void
{
- $this->uopzSetFunctionReturn('file_get_contents', function (string $file): string|false {
+ $this->setFunctionReturn('file_get_contents', function (string $file): string|false {
return str_contains($file, 'chrome-for-testing') ? '{}' : file_get_contents($file);
}, true);
@@ -249,7 +249,7 @@ public function should_throw_if_response_is_not_valid_json(): void
*/
public function should_throw_if_download_url_for_chrome_version_cannot_be_found_in_milestone_downloads(): void
{
- $this->uopzSetFunctionReturn('file_get_contents', function (string $file): string|false {
+ $this->setFunctionReturn('file_get_contents', function (string $file): string|false {
return str_contains($file, 'chrome-for-testing') ?
'{"milestones":{"116": {"downloads":{"chrome":{},"chromedriver":{}}}}}'
: file_get_contents($file);
@@ -271,8 +271,8 @@ public function should_throw_if_download_url_for_chrome_version_cannot_be_found_
*/
public function should_throw_if_existing_zip_file_cannot_be_removed(): void
{
- $this->uopzSetFunctionReturn('sys_get_temp_dir', codecept_output_dir());
- $this->uopzSetFunctionReturn('unlink', function (string $file): bool {
+ $this->setFunctionReturn('sys_get_temp_dir', codecept_output_dir());
+ $this->setFunctionReturn('unlink', function (string $file): bool {
return preg_match('~chromedriver\\.zip$~', $file) ? false : unlink($file);
}, true);
@@ -293,8 +293,8 @@ public function should_throw_if_existing_zip_file_cannot_be_removed(): void
public function should_throw_if_existing_binary_cannot_be_removed(): void
{
$dir = Filesystem::tmpDir('chromedriver_installer_', ['chromedriver' => '']);
- $this->uopzSetFunctionReturn('sys_get_temp_dir', codecept_output_dir());
- $this->uopzSetFunctionReturn('unlink', function (string $file) use ($dir): bool {
+ $this->setFunctionReturn('sys_get_temp_dir', codecept_output_dir());
+ $this->setFunctionReturn('unlink', function (string $file) use ($dir): bool {
return $file === $dir . '/chromedriver' ? false : unlink($file);
}, true);
@@ -314,8 +314,8 @@ public function should_throw_if_existing_binary_cannot_be_removed(): void
public function should_throw_if_new_binary_cannot_be_made_executable(): void
{
$dir = Filesystem::tmpDir('chromedriver_installer_');
- $this->uopzSetFunctionReturn('sys_get_temp_dir', codecept_output_dir());
- $this->uopzSetFunctionReturn('chmod', false);
+ $this->setFunctionReturn('sys_get_temp_dir', codecept_output_dir());
+ $this->setFunctionReturn('chmod', false);
$ci = new ChromedriverInstaller(null, 'linux64', codecept_data_dir('bins/chrome-mock'));
@@ -334,7 +334,7 @@ public function should_correctly_install_chromedriver(): void
{
$tmpDir = Filesystem::tmpDir('chromedriver_installer_tmp_');
$dir = Filesystem::tmpDir('chromedriver_installer_');
- $this->uopzSetFunctionReturn('sys_get_temp_dir', $tmpDir);
+ $this->setFunctionReturn('sys_get_temp_dir', $tmpDir);
$ci = new ChromedriverInstaller(null, 'linux64', codecept_data_dir('bins/chrome-mock'));
diff --git a/tests/unit/lucatume/WPBrowser/Utils/ComposerTest.php b/tests/unit/lucatume/WPBrowser/Utils/ComposerTest.php
index c4a13d62b..f59cf6667 100644
--- a/tests/unit/lucatume/WPBrowser/Utils/ComposerTest.php
+++ b/tests/unit/lucatume/WPBrowser/Utils/ComposerTest.php
@@ -6,9 +6,9 @@
use lucatume\WPBrowser\Adapters\Symfony\Component\Process\Process;
use lucatume\WPBrowser\Exceptions\RuntimeException;
use lucatume\WPBrowser\Tests\Traits\ClassStubs;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
-use PHPUnit\Framework\Assert;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Filesystem as FS;
+use PHPUnit\Framework\Assert;
class ComposerTest extends \Codeception\Test\Unit
{
@@ -34,7 +34,7 @@ public function should_throw_if_trying_to_build_on_non_existing_file(): void
*/
public function should_throw_if_trying_to_build_on_non_readable_file(): void
{
- $this->uopzSetFunctionReturn('is_readable', false);
+ $this->setFunctionReturn('is_readable', false);
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(Composer::ERR_FILE_NOT_FOUND);
new Composer(__FILE__);
@@ -78,7 +78,7 @@ public function should_throw_if_json_file_contents_cannot_be_decoded(): void
*/
public function should_throw_if_file_contents_cannot_be_read(): void
{
- $this->uopzSetFunctionReturn('file_get_contents', false);
+ $this->setFunctionReturn('file_get_contents', false);
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(Composer::ERR_FILE_UNREADABLE);
new Composer(__FILE__);
@@ -117,7 +117,7 @@ public function should_throw_if_write_fails(): void
$composer->requireDev(['foo/bar' => '1.0.0', 'foo/baz' => '^2.3']);
$hash = md5(microtime());
$outputFile = sys_get_temp_dir() . "/$hash-composer.json";
- $this->uopzSetFunctionReturn('file_put_contents', false);
+ $this->setFunctionReturn('file_put_contents', false);
$this->expectException(RuntimeException::class);
$this->expectExceptionCode(Composer::ERR_FILE_WRITE_FAILED);
$composer->write();
@@ -131,7 +131,7 @@ public function should_throw_if_write_fails(): void
public function should_allow_updating_the_composer_file(): void
{
$built = 0;
- $this->uopzSetMock(Process::class,
+ $this->setClassMock(Process::class,
$this->makeEmptyClass(Process::class, [
'__construct' => function () use (&$built) {
Assert::assertEquals(['composer', 'update', '--no-interaction'], func_get_args()[0]);
@@ -157,7 +157,7 @@ public function should_allow_updating_the_composer_file(): void
public function should_allow_updating_the_composer_file_for_a_specific_package(): void
{
$built = 0;
- $this->uopzSetMock(Process::class,
+ $this->setClassMock(Process::class,
$this->makeEmptyClass(Process::class, [
'__construct' => function () use (&$built) {
Assert::assertEquals(['composer', 'update', '--no-interaction', 'foo/baz'], func_get_args()[0]);
@@ -183,7 +183,7 @@ public function should_allow_updating_the_composer_file_for_a_specific_package()
*/
public function should_throw_if_update_fails(): void
{
- $this->uopzSetMock(Process::class,
+ $this->setClassMock(Process::class,
$this->makeEmptyClass(Process::class, [
'__construct' => function () {
Assert::assertEquals(['composer', 'update', '--no-interaction'], func_get_args()[0]);
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/Database/MysqlDatabaseTest.php b/tests/unit/lucatume/WPBrowser/WordPress/Database/MysqlDatabaseTest.php
index e20389512..bfd9f88fc 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/Database/MysqlDatabaseTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/Database/MysqlDatabaseTest.php
@@ -5,7 +5,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
@@ -17,7 +17,6 @@
use lucatume\WPBrowser\WordPress\InstallationState\Single;
use lucatume\WPBrowser\WordPress\WPConfigFile;
use PDO;
-use tad\Codeception\SnapshotAssertions\SnapshotAssertions;
class MysqlDatabaseTest extends Unit
{
@@ -175,7 +174,7 @@ public function should_throw_if_dump_cannot_be_opened_to_import(): void
$this->expectException(DbException::class);
$this->expectExceptionCode(DbException::DUMP_FILE_NOT_READABLE);
- $this->uopzSetFunctionReturn('fopen', false);
+ $this->setFunctionReturn('fopen', false);
$db->import(codecept_data_dir('files/test-dump-001.sql'));
}
@@ -279,7 +278,7 @@ public function should_throw_if_dump_file_cannot_be_exported(): void
$dbUser = Env::get('WORDPRESS_DB_USER');
$dbPassword = Env::get('WORDPRESS_DB_PASSWORD');
- $this->uopzSetFunctionReturn('fwrite', false);
+ $this->setFunctionReturn('fwrite', false);
$this->expectException(DbException::class);
$this->expectExceptionCode(DbException::FAILED_DUMP);
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/Database/SqliteDatabaseTest.php b/tests/unit/lucatume/WPBrowser/WordPress/Database/SqliteDatabaseTest.php
index 86419fcdc..3d8b36102 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/Database/SqliteDatabaseTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/Database/SqliteDatabaseTest.php
@@ -3,9 +3,9 @@
namespace lucatume\WPBrowser\WordPress\Database;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
-use lucatume\WPBrowser\WordPress\DbException;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Filesystem as FS;
+use lucatume\WPBrowser\WordPress\DbException;
use lucatume\WPBrowser\WordPress\Installation;
use lucatume\WPBrowser\WordPress\WPConfigFile;
@@ -33,7 +33,7 @@ public function should_throw_if_building_on_non_existing_directory(): void
*/
public function should_throw_if_building_on_non_writable_directory(): void
{
- $this->uopzSetFunctionReturn('is_writable', false);
+ $this->setFunctionReturn('is_writable', false);
$this->expectException(DbException::class);
$this->expectExceptionCode(SQLiteDatabase::ERR_DIR_NOT_FOUND);
new SQLiteDatabase(__DIR__);
@@ -140,7 +140,7 @@ public function should_throw_if_file_cannot_be_unlinked_during_drop(): void
$file = '/db.sqlite';
$db = new SQLiteDatabase($dir, $file);
$db->create();
- $this->uopzSetFunctionReturn('unlink', false);
+ $this->setFunctionReturn('unlink', false);
$this->expectException(DbException::class);
$this->expectExceptionCode(SQLiteDatabase::ERR_DROP_DB_FAILED);
$db->drop();
@@ -247,7 +247,7 @@ public function should_throw_if_trying_to_import_import_non_readable_file(): voi
$dump = codecept_data_dir('dump.sqlite');
$dir = FS::tmpDir('sqlite_');
$file = 'db.sqlite';
- $this->uopzSetFunctionReturn('file_get_contents', false);
+ $this->setFunctionReturn('file_get_contents', false);
$this->expectException(DbException::class);
$this->expectExceptionCode(DbException::DUMP_FILE_NOT_READABLE);
@@ -334,7 +334,7 @@ public function should_throw_if_database_dump_file_cannot_be_written(): void
$db = new SQLiteDatabase($dir, $file);
$db->import($dump);
- $this->uopzSetFunctionReturn('file_put_contents', false);
+ $this->setFunctionReturn('file_put_contents', false);
$this->expectException(DbException::class);
$this->expectExceptionCode(DbException::FAILED_DUMP);
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/ConfiguredTest.php b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/ConfiguredTest.php
index 0399bf959..0b9ab3f71 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/ConfiguredTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/ConfiguredTest.php
@@ -7,7 +7,7 @@
use Exception;
use lucatume\WPBrowser\Tests\Traits\ClassStubs;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/EmptyDirTest.php b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/EmptyDirTest.php
index 6628cc74d..25cabafdc 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/EmptyDirTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/EmptyDirTest.php
@@ -5,7 +5,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/MultisiteTest.php b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/MultisiteTest.php
index c70282515..d713b9622 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/MultisiteTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/MultisiteTest.php
@@ -6,7 +6,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Exceptions\InvalidArgumentException;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/ScaffoldedTest.php b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/ScaffoldedTest.php
index 9a15d9d4c..2f63fe2ba 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/ScaffoldedTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/ScaffoldedTest.php
@@ -5,7 +5,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
@@ -656,7 +656,7 @@ public function should_throw_if_sqlite_plugin_db_copy_file_cannot_be_read(): voi
$scaffolded = new Scaffolded($wpRootDir);
- $this->uopzSetFunctionReturn('file_get_contents', function (string $file) {
+ $this->setFunctionReturn('file_get_contents', function (string $file) {
if (str_ends_with($file, 'db.copy')) {
return false;
}
@@ -681,7 +681,7 @@ public function should_throw_if_sqlite_drop_in_placement_fails(): void
$scaffolded = new Scaffolded($wpRootDir);
- $this->uopzSetFunctionReturn('file_put_contents', function (string $file) {
+ $this->setFunctionReturn('file_put_contents', function (string $file) {
if (str_ends_with($file, 'db.php')) {
return false;
}
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/SingleTest.php b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/SingleTest.php
index bd329c725..0862f4c97 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/SingleTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/InstallationState/SingleTest.php
@@ -6,7 +6,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Exceptions\InvalidArgumentException;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
@@ -238,7 +238,7 @@ public function should_throw_if_wp_config_php_file_contents_cannot_be_read_durin
$single = new Single($wpRootDir, $wpRootDir . '/wp-config.php');
- $this->uopzSetFunctionReturn('file_get_contents', false);
+ $this->setFunctionReturn('file_get_contents', false);
$this->expectException(InstallationException::class);
$this->expectExceptionCode(InstallationException::WP_CONFIG_FILE_NOT_FOUND);
@@ -272,7 +272,7 @@ public function should_throw_if_the_placeholder_is_not_found_in_the_wp_config_ph
$single = new Single($wpRootDir, $wpConfigFilePath);
- $this->uopzSetFunctionReturn('file_get_contents', function (string $file) use ($wpConfigFilePath) {
+ $this->setFunctionReturn('file_get_contents', function (string $file) use ($wpConfigFilePath) {
if ($file === $wpConfigFilePath) {
return 'uopzSetFunctionReturn('file_put_contents', false);
+ $this->setFunctionReturn('file_put_contents', false);
$this->expectException(InstallationException::class);
$this->expectExceptionCode(InstallationException::WRITE_ERROR);
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/InstallationTest.php b/tests/unit/lucatume/WPBrowser/WordPress/InstallationTest.php
index 3a3a27998..5f3235ede 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/InstallationTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/InstallationTest.php
@@ -4,10 +4,9 @@
namespace lucatume\WPBrowser\WordPress;
use Codeception\Test\Unit;
-use lucatume\WPBrowser\Tests\FSTemplates\BedrockProject;
use lucatume\WPBrowser\Tests\Traits\MainInstallationAccess;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\Utils\Random;
@@ -46,7 +45,7 @@ public function should_throw_when_building_on_non_existing_root_directory(): voi
public function should_throw_when_building_on_non_writable_root_directory(): void
{
$tmpDir = FS::tmpDir('installation_');
- $this->uopzSetFunctionReturn('is_writable',
+ $this->setFunctionReturn('is_writable',
fn(string $dir) => !($dir === $tmpDir . '/') && is_writable($dir),
true
);
@@ -65,7 +64,7 @@ public function should_throw_when_building_on_non_writable_root_directory(): voi
public function should_throw_when_building_on_non_readable_root_directory()
{
$tmpDir = FS::tmpDir('installation_');
- $this->uopzSetFunctionReturn('is_readable',
+ $this->setFunctionReturn('is_readable',
fn(string $dir) => !($dir === $tmpDir . '/') && is_readable($dir),
true
);
diff --git a/tests/unit/lucatume/WPBrowser/WordPress/WpConfigFileGeneratorTest.php b/tests/unit/lucatume/WPBrowser/WordPress/WpConfigFileGeneratorTest.php
index 227477279..77f1b42d6 100644
--- a/tests/unit/lucatume/WPBrowser/WordPress/WpConfigFileGeneratorTest.php
+++ b/tests/unit/lucatume/WPBrowser/WordPress/WpConfigFileGeneratorTest.php
@@ -4,7 +4,7 @@
use Codeception\Test\Unit;
use lucatume\WPBrowser\Tests\Traits\TmpFilesCleanup;
-use lucatume\WPBrowser\Tests\Traits\UopzFunctions;
+use lucatume\WPBrowser\Traits\UopzFunctions;
use lucatume\WPBrowser\Utils\Env;
use lucatume\WPBrowser\Utils\Filesystem as FS;
use lucatume\WPBrowser\WordPress\Database\MysqlDatabase;
@@ -58,7 +58,7 @@ public function should_throw_if_wp_config_sample_php_file_cannot_be_read(): void
'wp-settings.php' => ' 'uopzSetFunctionReturn('file_get_contents', static function ($file) use ($wpRootDir) {
+ $this->setFunctionReturn('file_get_contents', static function ($file) use ($wpRootDir) {
return $file === $wpRootDir . '/wp-config-sample.php' ? false : file_get_contents($file);
}, true);
diff --git a/tests/wploadersuite/AjaxTest.php b/tests/wploadersuite/AjaxTest.php
index c5399bdb4..c8fd7a410 100644
--- a/tests/wploadersuite/AjaxTest.php
+++ b/tests/wploadersuite/AjaxTest.php
@@ -1,7 +1,7 @@