Created
August 30, 2015 10:15
-
-
Save tellyworth/9b37e852c34ed9fc4197 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* @group option | |
* @ticket 31245 | |
*/ | |
class Tests_Option_Alloptions extends WP_UnitTestCase { | |
/** | |
* Some cache implementations have a 1mb limit per-key. This shouldn't be an issue when get_multi() is in use, but this provides prior coverage. | |
*/ | |
function test_alloptions_1mb() { | |
$big_val = str_repeat( 'a', 1024*1024+1 ); // make sure we exceed 1mb | |
wp_cache_flush(); | |
update_option( __FUNCTION__, $big_val, true ); // autoload = true | |
wp_cache_flush(); | |
wp_load_alloptions(); | |
global $wpdb; | |
$before = $wpdb->num_queries; | |
$value = get_option( __FUNCTION__ ); | |
$after = $wpdb->num_queries; | |
$this->assertEquals( $before, $after ); | |
$this->assertEquals( $value, $big_val ); // cached | |
// clean up | |
delete_option( __FUNCTION__ ); | |
} | |
/** | |
* Make sure wp_load_alloptions() correctly autoloads options such that get_option() does not hit the db. | |
*/ | |
function test_load_alloptions() { | |
global $wpdb; | |
$autoload_keys = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE autoload = %s", 'yes' ) ); | |
wp_cache_flush(); | |
wp_load_alloptions(); | |
$before = $wpdb->num_queries; | |
// Fetch all of the autoload options one by one | |
foreach( $autoload_keys as $key ) { | |
$value = get_option( $key ); | |
$this->assertNotSame( $value, false ); | |
} | |
// Should have resulted in zero queries | |
$after = $wpdb->num_queries; | |
$this->assertEquals( $before, $after ); | |
} | |
/** | |
* Make sure wp_load_alloptions() only fetches autoload=yes. | |
*/ | |
function test_load_not_alloptions() { | |
global $wpdb; | |
$not_autoload_keys = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM $wpdb->options WHERE autoload = %s", 'no' ) ); | |
wp_cache_flush(); | |
wp_load_alloptions(); | |
$before = $wpdb->num_queries; | |
// Fetch all of the autoload options one by one | |
foreach( $not_autoload_keys as $key ) { | |
$value = get_option( $key ); | |
$this->assertNotSame( $value, false ); | |
} | |
// Should have resulted in zero queries | |
$after = $wpdb->num_queries; | |
$this->assertEquals( $before + count($not_autoload_keys), $after ); | |
} | |
/** | |
* Simulate a race condition like those described in #31245 and #25623 | |
* This test requires a memcached plugin/dropin; assuming one similar to Boren's plugin, hence: | |
* @requires function WP_Object_Cache::get_mc | |
*/ | |
function test_autoload_concurrency_failure() { | |
// A separate object cache instance with a separate local cache array, to simulate a second process. | |
$other_process_cache = new WP_Object_Cache(); | |
$value = 'Original value.'; | |
update_option( __FUNCTION__, $value , true ); // autoload = true | |
wp_load_alloptions(); | |
// Simulate a second process changing the option in db and memcache. | |
$value2 = 'Changed in second process.'; | |
global $wp_object_cache; | |
$old_wp_object_cache = $wp_object_cache; | |
$wp_object_cache = $other_process_cache; | |
// In the second "process", change the option value, and make sure it stuck. | |
wp_load_alloptions(); | |
update_option( __FUNCTION__, $value2, true); | |
$this->assertEquals( $value2, get_option( __FUNCTION__) ); | |
$wp_object_cache = $old_wp_object_cache; | |
// Now we change a different autoload option. This will clobber the first option in cache and db. | |
update_option( __FUNCTION__ . '_2', 'An entirely separate option.', true); | |
// If not for the clobbering bug, this would correctly refresh from memcache/db. | |
global $wp_object_cache; | |
$wp_object_cache->cache = array(); | |
$actual = get_option( __FUNCTION__ ); | |
$this->assertEquals( $value2, $actual ); | |
delete_option( __FUNCTION__ ); | |
delete_option( __FUNCTION__ . '_2' ); | |
} | |
/** | |
* Similar to test_autoload_concurrency_failure but without the clobbering update. | |
* This is a negative test, just to make sure the previous test is successfully simulating the race condition. | |
* @requires function WP_Object_Cache::get_mc | |
*/ | |
function test_autoload_concurrency_pass() { | |
// A separate object cache instance with a separate local cache array, to simulate a second process. | |
$other_process_cache = new WP_Object_Cache(); | |
$value = 'Original value.'; | |
update_option( __FUNCTION__, $value , true ); // autoload = true | |
wp_load_alloptions(); | |
// Simulate a second process changing the option in db and memcache. | |
$value2 = 'Changed in second process.'; | |
global $wp_object_cache; | |
$old_wp_object_cache = $wp_object_cache; | |
$wp_object_cache = $other_process_cache; | |
// In the second "process", change the option value, and make sure it stuck. | |
wp_load_alloptions(); | |
update_option( __FUNCTION__, $value2, true); | |
$this->assertEquals( $value2, get_option( __FUNCTION__) ); | |
$wp_object_cache = $old_wp_object_cache; | |
// DON'T clobber the cache with an update_option() call this time. | |
// If not for the clobbering bug, this would correctly refresh from memcache/db. | |
global $wp_object_cache; | |
$wp_object_cache->cache = array(); | |
$actual = get_option( __FUNCTION__ ); | |
$this->assertEquals( $value2, $actual ); | |
delete_option( __FUNCTION__ ); | |
delete_option( __FUNCTION__ . '_2' ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment