Make WordPress Core


Ignore:
Timestamp:
10/03/2023 02:09:07 PM (3 years ago)
Author:
flixos90
Message:

Options, Meta APIs: Implement additional tests covering update_option().

As a follow up to [56681], this changeset adds further tests, primarily focused on ensuring no unnecessary database queries are run.

Props mukesh27, costdev, joemcgill, spacedmonkey.
See #22192.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/option/option.php

    r56717 r56762  
    371371
    372372        /**
    373          * Ensure the database is getting updated when type changes, but not otherwise.
    374          *
    375          * @ticket 22192
    376          *
    377          * @covers ::update_option
    378          *
    379          * @dataProvider data_update_option_type_juggling
    380          */
    381         public function test_update_loosey_options( $old_value, $new_value, $update = false ) {
     373         * Tests that update_option() triggers one additional query and returns true
     374         * for some loosely equal old and new values when the old value is retrieved from the cache.
     375         *
     376         * The additional query is triggered to update the value in the database.
     377         *
     378         * @ticket 22192
     379         *
     380         * @covers ::update_option
     381         *
     382         * @dataProvider data_loosely_equal_values_that_should_update
     383         *
     384         * @param mixed $old_value The old value.
     385         * @param mixed $new_value The new value to try to set.
     386         */
     387        public function test_update_option_should_update_some_loosely_equal_values_from_cache( $old_value, $new_value ) {
    382388                add_option( 'foo', $old_value );
     389
     390                $num_queries = get_num_queries();
    383391
    384392                // Comparison will happen against value cached during add_option() above.
    385393                $updated = update_option( 'foo', $new_value );
    386394
    387                 if ( $update ) {
    388                         $this->assertTrue( $updated, 'This loosely equal option should trigger an update.' );
    389                 } else {
    390                         $this->assertFalse( $updated, 'Loosely equal option should not trigger an update.' );
    391                 }
    392         }
    393 
    394         /**
    395          * Ensure the database is getting updated when type changes, but not otherwise.
    396          *
    397          * @ticket 22192
    398          *
    399          * @covers ::update_option
    400          *
    401          * @dataProvider data_update_option_type_juggling
    402          */
    403         public function test_update_loosey_options_from_db( $old_value, $new_value, $update = false ) {
     395                $this->assertSame( 1, get_num_queries() - $num_queries, 'One additional query should have run to update the value.' );
     396                $this->assertTrue( $updated, 'update_option() should have returned true.' );
     397        }
     398
     399        /**
     400         * Tests that update_option() triggers two additional queries and returns true
     401         * for some loosely equal old and new values when the old value is retrieved from the database.
     402         *
     403         * The two additional queries are triggered to:
     404         * 1. retrieve the old value from the database, as the option does not exist in the cache.
     405         * 2. update the value in the database.
     406         *
     407         * @ticket 22192
     408         *
     409         * @covers ::update_option
     410         *
     411         * @dataProvider data_loosely_equal_values_that_should_update
     412         *
     413         * @param mixed $old_value The old value.
     414         * @param mixed $new_value The new value to try to set.
     415         */
     416        public function test_update_option_should_update_some_loosely_equal_values_from_db( $old_value, $new_value ) {
    404417                add_option( 'foo', $old_value );
     418
     419                $num_queries = get_num_queries();
    405420
    406421                // Delete cache.
    … …  
    408423                $updated = update_option( 'foo', $new_value );
    409424
    410                 if ( $update ) {
    411                         $this->assertTrue( $updated, 'This loosely equal option should trigger an update.' );
    412                 } else {
    413                         $this->assertFalse( $updated, 'Loosely equal option should not trigger an update.' );
    414                 }
    415         }
    416 
    417         /**
    418          * Ensure the database is getting updated when type changes, but not otherwise.
    419          *
    420          * @ticket 22192
    421          *
    422          * @covers ::update_option
    423          *
    424          * @dataProvider data_update_option_type_juggling
    425          */
    426         public function test_update_loosey_options_from_refreshed_cache( $old_value, $new_value, $update = false ) {
     425                $this->assertSame( 2, get_num_queries() - $num_queries, 'Two additional queries should have run.' );
     426                $this->assertTrue( $updated, 'update_option() should have returned true.' );
     427        }
     428
     429        /**
     430         * Tests that update_option() triggers one additional query and returns true
     431         * for some loosely equal old and new values when the old value is retrieved from a refreshed cache.
     432         *
     433         * The additional query is triggered to update the value in the database.
     434         *
     435         * @ticket 22192
     436         *
     437         * @covers ::update_option
     438         *
     439         * @dataProvider data_loosely_equal_values_that_should_update
     440         *
     441         * @param mixed $old_value The old value.
     442         * @param mixed $new_value The new value to try to set.
     443         */
     444        public function test_update_option_should_update_some_loosely_equal_values_from_refreshed_cache( $old_value, $new_value ) {
    427445                add_option( 'foo', $old_value );
    428446
    … …  
    431449                wp_load_alloptions();
    432450
     451                $num_queries = get_num_queries();
     452                $updated     = update_option( 'foo', $new_value );
     453
     454                $this->assertSame( 1, get_num_queries() - $num_queries, 'One additional query should have run to update the value.' );
     455                $this->assertTrue( $updated, 'update_option() should have returned true.' );
     456        }
     457
     458        /**
     459         * Data provider.
     460         *
     461         * @return array
     462         */
     463        public function data_loosely_equal_values_that_should_update() {
     464                return array(
     465                        // Falsey values.
     466                        '(string) "0" to false'       => array( '0', false ),
     467                        'empty string to (int) 0'     => array( '', 0 ),
     468                        'empty string to (float) 0.0' => array( '', 0.0 ),
     469                        '(int) 0 to empty string'     => array( 0, '' ),
     470                        '(int) 0 to false'            => array( 0, false ),
     471                        '(float) 0.0 to empty string' => array( 0.0, '' ),
     472                        '(float) 0.0 to false'        => array( 0.0, false ),
     473                        'false to (string) "0"'       => array( false, '0' ),
     474                        'false to (int) 0'            => array( false, 0 ),
     475                        'false to (float) 0.0'        => array( false, 0.0 ),
     476
     477                        // Non-scalar values.
     478                        'false to array()'            => array( false, array() ),
     479                        '(string) "false" to array()' => array( 'false', array() ),
     480                        'empty string to array()'     => array( '', array() ),
     481                        '(int 0) to array()'          => array( 0, array() ),
     482                        '(string) "0" to array()'     => array( '0', array() ),
     483                        '(string) "false" to null'    => array( 'false', null ),
     484                        '(int) 0 to null'             => array( 0, null ),
     485                        '(string) "0" to null'        => array( '0', null ),
     486                        'array() to false'            => array( array(), false ),
     487                        'array() to (string) "false"' => array( array(), 'false' ),
     488                        'array() to empty string'     => array( array(), '' ),
     489                        'array() to (int) 0'          => array( array(), 0 ),
     490                        'array() to (string) "0"'     => array( array(), '0' ),
     491                        'array() to null'             => array( array(), null ),
     492                );
     493        }
     494
     495        /**
     496         * Tests that update_option() triggers no additional queries and returns false
     497         * for some values when the old value is retrieved from the cache.
     498         *
     499         * @ticket 22192
     500         *
     501         * @covers ::update_option
     502         *
     503         * @dataProvider data_loosely_equal_values_that_should_not_update
     504         * @dataProvider data_strictly_equal_values
     505         *
     506         * @param mixed $old_value The old value.
     507         * @param mixed $new_value The new value to try to set.
     508         */
     509        public function test_update_option_should_not_update_some_values_from_cache( $old_value, $new_value ) {
     510                add_option( 'foo', $old_value );
     511
     512                $num_queries = get_num_queries();
     513
     514                // Comparison will happen against value cached during add_option() above.
    433515                $updated = update_option( 'foo', $new_value );
    434516
    435                 if ( $update ) {
    436                         $this->assertTrue( $updated, 'This loosely equal option should trigger an update.' );
    437                 } else {
    438                         $this->assertFalse( $updated, 'Loosely equal option should not trigger an update.' );
    439                 }
     517                $this->assertSame( $num_queries, get_num_queries(), 'No additional queries should have run.' );
     518                $this->assertFalse( $updated, 'update_option() should have returned false.' );
     519        }
     520
     521        /**
     522         * Tests that update_option() triggers one additional query and returns false
     523         * for some values when the old value is retrieved from the database.
     524         *
     525         * The additional query is triggered to retrieve the old value from the database.
     526         *
     527         * @ticket 22192
     528         *
     529         * @covers ::update_option
     530         *
     531         * @dataProvider data_loosely_equal_values_that_should_not_update
     532         * @dataProvider data_strictly_equal_values
     533         *
     534         * @param mixed $old_value The old value.
     535         * @param mixed $new_value The new value to try to set.
     536         */
     537        public function test_update_option_should_not_update_some_values_from_db( $old_value, $new_value ) {
     538                add_option( 'foo', $old_value );
     539
     540                $num_queries = get_num_queries();
     541
     542                // Delete cache.
     543                wp_cache_delete( 'alloptions', 'options' );
     544                $updated = update_option( 'foo', $new_value );
     545
     546                $this->assertSame( 1, get_num_queries() - $num_queries, 'One additional query should have run.' );
     547                $this->assertFalse( $updated, 'update_option() should have returned false.' );
     548        }
     549
     550        /**
     551         * Tests that update_option() triggers no additional queries and returns false
     552         * for some values when the old value is retrieved from a refreshed cache.
     553         *
     554         * @ticket 22192
     555         *
     556         * @covers ::update_option
     557         *
     558         * @dataProvider data_loosely_equal_values_that_should_not_update
     559         * @dataProvider data_strictly_equal_values
     560         *
     561         * @param mixed $old_value The old value.
     562         * @param mixed $new_value The new value to try to set.
     563         */
     564        public function test_update_option_should_not_update_some_values_from_refreshed_cache( $old_value, $new_value ) {
     565                add_option( 'foo', $old_value );
     566
     567                // Delete and refresh cache from DB.
     568                wp_cache_delete( 'alloptions', 'options' );
     569                wp_load_alloptions();
     570
     571                $num_queries = get_num_queries();
     572                $updated     = update_option( 'foo', $new_value );
     573
     574                $this->assertSame( $num_queries, get_num_queries(), 'No additional queries should have run.' );
     575                $this->assertFalse( $updated, 'update_option() should have returned false.' );
    440576        }
    441577
    … …  
    443579         * Data provider.
    444580         *
     581         * @return array[]
     582         */
     583        public function data_loosely_equal_values_that_should_not_update() {
     584                return array(
     585                        // Truthy values.
     586                        '(string) "1" to (int) 1'     => array( '1', 1 ),
     587                        '(string) "1" to (float) 1.0' => array( '1', 1.0 ),
     588                        '(string) "1" to true'        => array( '1', true ),
     589                        '(int) 1 to (string) "1"'     => array( 1, '1' ),
     590                        '1 to (float) 1.0'            => array( 1, 1.0 ),
     591                        '(int) 1 to true'             => array( 1, true ),
     592                        '(float) 1.0 to (string) "1"' => array( 1.0, '1' ),
     593                        '(float) 1.0 to (int) 1'      => array( 1.0, 1 ),
     594                        '1.0 to true'                 => array( 1.0, true ),
     595                        'true to (string) "1"'        => array( true, '1' ),
     596                        'true to 1'                   => array( true, 1 ),
     597                        'true to (float) 1.0'         => array( true, 1.0 ),
     598
     599                        // Falsey values.
     600                        '(string) "0" to (int) 0'     => array( '0', 0 ),
     601                        '(string) "0" to (float) 0.0' => array( '0', 0.0 ),
     602                        '(int) 0 to (string) "0"'     => array( 0, '0' ),
     603                        '(int) 0 to (float) 0.0'      => array( 0, 0.0 ),
     604                        '(float) 0.0 to (string) "0"' => array( 0.0, '0' ),
     605                        '(float) 0.0 to (int) 0'      => array( 0.0, 0 ),
     606                        'empty string to false'       => array( '', false ),
     607                        'empty string to null'        => array( '', null ),
     608
     609                        /*
     610                         * null as an initial value behaves differently by triggering
     611                         * a query, so it is not included in these datasets.
     612                         *
     613                         * See data_stored_as_empty_string() and its related test.
     614                         */
     615                );
     616        }
     617
     618        /**
     619         * Data provider.
     620         *
    445621         * @return array
    446622         */
    447         public function data_update_option_type_juggling() {
     623        public function data_strictly_equal_values() {
     624                $obj = new stdClass();
     625
    448626                return array(
     627                        // Truthy values.
     628                        '(string) "1"'       => array( '1', '1' ),
     629                        '(int) 1'            => array( 1, 1 ),
     630                        '(float) 1.0'        => array( 1.0, 1.0 ),
     631                        'true'               => array( true, true ),
     632                        'string with spaces' => array( ' ', ' ' ),
     633                        'non-empty array'    => array( array( 'false' ), array( 'false' ) ),
     634                        'object'             => array( $obj, $obj ),
     635
     636                        // Falsey values.
     637                        '(string) "0"'       => array( '0', '0' ),
     638                        'empty string'       => array( '', '' ),
     639                        '(int) 0'            => array( 0, 0 ),
     640                        '(float) 0.0'        => array( 0.0, 0.0 ),
     641                        'empty array'        => array( array(), array() ),
     642
    449643                        /*
    450                          * Truthy values.
    451                          * Loosely equal truthy scalar values should never result in a DB update.
     644                         * false and null are not included in these datasets
     645                         * because false is the default value, which triggers
     646                         * a call to add_option().
     647                         *
     648                         * See data_stored_as_empty_string() and its related test.
    452649                         */
    453                         array( '1', '1' ),
    454                         array( '1', 1 ),
    455                         array( '1', 1.0 ),
    456                         array( '1', true ),
    457                         array( 1, '1' ),
    458                         array( 1, 1 ),
    459                         array( 1, 1.0 ),
    460                         array( 1, true ),
    461                         array( 1.0, '1' ),
    462                         array( 1.0, 1 ),
    463                         array( 1.0, 1.0 ),
    464                         array( 1.0, true ),
    465                         array( true, '1' ),
    466                         array( true, 1 ),
    467                         array( true, 1.0 ),
    468                         array( true, true ),
    469 
    470                         /*
    471                          * Falsey values.
    472                          * Loosely equal falsey scalar values only sometimes result in a DB update.
    473                          */
    474                         array( '0', '0' ),
    475                         array( '0', 0 ),
    476                         array( '0', 0.0 ),
    477                         array( '0', false, true ), // Should update.
    478                         array( '', '' ),
    479                         array( '', 0, true ), // Should update.
    480                         array( '', 0.0, true ), // Should update.
    481                         array( '', false ),
    482                         array( 0, '0' ),
    483                         array( 0, '', true ), // Should update.
    484                         array( 0, 0 ),
    485                         array( 0, 0.0 ),
    486                         array( 0, false, true ), // Should update.
    487                         array( 0.0, '0' ),
    488                         array( 0.0, '', true ), // Should update.
    489                         array( 0.0, 0 ),
    490                         array( 0.0, 0.0 ),
    491                         array( 0.0, false, true ), // Should update.
    492                         array( false, '0', true ), // Should update.
    493                         array( false, '' ),
    494                         array( false, 0, true ), // Should update.
    495                         array( false, 0.0, true ), // Should update.
    496                         array( false, false ),
    497 
    498                         /*
    499                          * Non scalar values.
    500                          * Loosely equal non-scalar values should almost always result in an update.
    501                          */
    502                         array( false, array(), true ),
    503                         array( 'false', array(), true ),
    504                         array( '', array(), true ),
    505                         array( 0, array(), true ),
    506                         array( '0', array(), true ),
    507                         array( false, null ), // Does not update.
    508                         array( 'false', null, true ),
    509                         array( '', null ), // Does not update.
    510                         array( 0, null, true ),
    511                         array( '0', null, true ),
    512                         array( array(), false, true ),
    513                         array( array(), 'false', true ),
    514                         array( array(), '', true ),
    515                         array( array(), 0, true ),
    516                         array( array(), '0', true ),
    517                         array( array(), null, true ),
    518                         array( null, false ), // Does not update.
    519                         array( null, 'false', true ),
    520                         array( null, '' ), // Does not update.
    521                         array( null, 0, true ),
    522                         array( null, '0', true ),
    523                         array( null, array(), true ),
    524                 );
    525         }
    526 
    527         /**
    528          * Tests that update_option() stores an option that uses
    529          * an unfiltered default value of (bool) false.
    530          *
    531          * @ticket 22192
    532          *
    533          * @covers ::update_option
    534          */
    535         public function test_update_option_should_store_option_with_default_value_false() {
     650                );
     651        }
     652
     653        /**
     654         * Tests that update_option() adds a non-existent option when the new value
     655         * is stored as an empty string and false is the default value for the option.
     656         *
     657         * @ticket 22192
     658         *
     659         * @dataProvider data_stored_as_empty_string
     660         *
     661         * @param mixed $new_value A value that casts to an empty string.
     662         */
     663        public function test_update_option_should_add_option_when_the_new_value_is_stored_as_an_empty_string_and_matches_default_value_false( $new_value ) {
    536664                global $wpdb;
    537665
    538                 $option = 'update_option_default_false';
    539                 update_option( $option, false );
    540 
    541                 $actual = $wpdb->query(
    542                         $wpdb->prepare(
    543                                 "SELECT option_name FROM $wpdb->options WHERE option_name = %s LIMIT 1",
    544                                 $option
    545                         )
    546                 );
    547 
    548                 $this->assertSame( 1, $actual );
    549         }
    550 
    551         /**
    552          * Tests that update_option() stores an option that uses
    553          * a filtered default value.
    554          *
    555          * @ticket 22192
    556          *
    557          * @covers ::update_option
    558          */
    559         public function test_update_option_should_store_option_with_filtered_default_value() {
     666                $this->assertTrue( update_option( 'foo', $new_value ), 'update_option() should have returned true.' );
     667
     668                $actual = $wpdb->get_row( "SELECT option_value FROM $wpdb->options WHERE option_name = 'foo' LIMIT 1" );
     669
     670                $this->assertIsObject( $actual, 'The option was not added to the database.' );
     671                $this->assertObjectHasProperty( 'option_value', $actual, 'The "option_value" property was not included.' );
     672                $this->assertSame( '', $actual->option_value, 'The value was not stored as an empty string.' );
     673        }
     674
     675        /**
     676         * Data provider.
     677         *
     678         * @return array[]
     679         */
     680        public function data_stored_as_empty_string() {
     681                return array(
     682                        'false'        => array( false ),
     683                        'empty string' => array( '' ),
     684                        'null'         => array( null ),
     685                );
     686        }
     687
     688        /**
     689         * Tests that update_option() adds a non-existent option that uses a filtered default value.
     690         *
     691         * @ticket 22192
     692         *
     693         * @covers ::update_option
     694         */
     695        public function test_update_option_should_add_option_with_filtered_default_value() {
    560696                global $wpdb;
    561697
    … …  
    570706                );
    571707
    572                 update_option( $option, $default_value );
    573 
    574                 $actual = $wpdb->query(
     708                $this->assertTrue( update_option( $option, $default_value ), 'update_option() should have returned true.' );
     709
     710                $actual = $wpdb->get_row(
    575711                        $wpdb->prepare(
    576                                 "SELECT option_name FROM $wpdb->options WHERE option_name = %s LIMIT 1",
     712                                "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
    577713                                $option
    578714                        )
    579715                );
    580716
    581                 $this->assertSame( 1, $actual );
    582         }
    583 
    584         /**
    585          * Tests that a non-existing option is added even when its pre filter returns a value.
     717                $this->assertIsObject( $actual, 'The option was not added to the database.' );
     718                $this->assertObjectHasProperty( 'option_value', $actual, 'The "option_value" property was not included.' );
     719                $this->assertSame( $default_value, $actual->option_value, 'The value was not stored as an empty string.' );
     720        }
     721
     722        /**
     723         * Tests that a non-existent option is added even when its pre filter returns a value.
    586724         *
    587725         * @ticket 22192
Note: See TracChangeset for help on using the changeset viewer.