Make WordPress Core

Changeset 56762


Ignore:
Timestamp:
10/03/2023 02:09:07 PM (15 months 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.