Completed
Push — renovate/react-monorepo ( 545e89...65a81e )
by
unknown
268:48 queued 253:47
created

WP_Test_Jetpack_Sync_Functions   F

Complexity

Total Complexity 102

Size/Duplication

Total Lines 1285
Duplicated Lines 11.75 %

Coupling/Cohesion

Components 2
Dependencies 20

Importance

Changes 0
Metric Value
dl 151
loc 1285
rs 0.8
c 0
b 0
f 0
wmc 102
lcom 2
cbo 20

74 Methods

Rating   Name   Duplication   Size   Complexity  
A test_class.jetpack-sync-callables.php ➔ is_wpe() 0 3 1
A setUp() 0 8 1
A test_white_listed_function_is_synced() 8 8 1
A test_sync_jetpack_updates() 0 5 1
A test_wp_version_is_synced() 0 6 1
B test_sync_callable_whitelist() 0 85 5
A add_test_block() 0 3 1
A assertCallableIsSynced() 0 3 1
A test_white_listed_callables_doesnt_get_synced_twice() 0 16 1
A test_white_listed_callable_sync_on_next_tick() 0 17 1
A test_updating_stylesheet_sends_the_theme_data() 0 12 1
A test_sync_always_sync_changes_to_modules_right_away() 0 16 1
A test_sync_always_sync_changes_to_home_siteurl_right_away() 0 32 1
A test_sync_jetpack_sync_unlock_sync_callable_action_allows_syncing_siteurl_changes() 0 41 1
A test_home_site_urls_synced_while_migrate_for_idc_set() 0 37 1
A return_example_com() 0 3 1
A return_example_com_blog() 0 3 1
A return_https_example_com() 0 3 1
A return_https_example_org() 0 3 1
A return_site_com() 0 3 1
A return_https_site_com() 0 3 1
A return_https_site_com_blog() 0 3 1
A return_https_www_example_com() 0 3 1
A return_https_foo_example_com() 0 3 1
A test_get_protocol_normalized_url_works_with_no_history() 0 21 1
A test_get_protocol_normalized_url_stores_max_history() 0 11 2
A test_get_protocol_normalized_url_returns_http_when_https_falls_off() 0 25 2
A test_get_protocol_normalized_url_returns_new_value_cannot_parse() 0 7 1
A test_get_protocol_normalized_url_cleared_on_reset_data() 0 16 3
A test_subdomain_switching_to_www_does_not_cause_sync() 0 22 1
A test_sync_limited_set_of_callables_if_cron() 27 27 5
A test_sync_limited_set_of_callables_if_wp_cli() 27 27 5
A test_site_icon_url_returns_false_when_no_site_icon() 0 5 1
A test_site_icon_url_returns_core_site_icon_url_when_set() 0 15 1
A test_site_icon_url_fallback_to_jetpack_site_icon_url() 0 7 1
A test_calling_taxonomies_do_not_modify_global() 0 14 1
A test_sanitize_sync_taxonomies_method() 0 21 1
A test_sanitize_sync_post_type_method_default() 13 13 1
A test_sanitize_sync_post_type_method_remove_unknown_values_set() 12 12 1
A assert_sanitized_post_type_default() 0 30 1
A test_sanitize_sync_post_type_method_all_values_set() 0 45 3
A test_get_post_types_method() 0 12 3
A test_register_post_types_callback_error() 0 7 1
A test_get_raw_url_by_option_bypasses_filters() 0 5 1
A test_get_raw_url_by_constant_bypasses_filters() 0 18 2
A test_get_raw_url_returns_with_http_if_is_ssl() 14 14 1
A test_raw_home_url_is_https_when_is_ssl() 19 19 1
A test_user_can_stop_raw_urls() 0 15 1
B test_plugin_action_links_get_synced() 0 59 3
A extract_plugins_we_are_testing() 0 11 5
A cause_fatal_error() 0 5 1
A test_fixes_fatal_error() 0 14 1
A return_filtered_url() 0 3 1
A add_www_subdomain_to_siteurl() 0 5 1
A test_taxonomies_objects_do_not_have_meta_box_callback() 0 30 2
A test_force_sync_callable_on_plugin_update() 0 36 1
A test_xml_rpc_request_callables_has_actor() 0 18 1
A mock_authenticated_xml_rpc() 0 39 1
A mock_authenticated_xml_rpc_cleanup() 0 20 1
A mock_jetpack_private_options() 7 7 1
A test_get_timezone_from_timezone_string() 0 5 1
A test_get_timezone_from_gmt_offset_zero() 0 5 1
A test_get_timezone_from_gmt_offset_plus() 0 5 1
A test_get_timezone_from_gmt_offset_fractions() 0 5 1
A test_get_timezone_from_gmt_offset_minus() 0 5 1
A test_sync_callable_recursive_gets_checksum() 7 7 1
A test_get_hosting_provider_callable_with_unknown_host() 0 3 1
A test_get_hosting_provider_by_known_constant() 0 10 1
A test_get_hosting_provider_by_known_class() 0 11 1
A test_get_hosting_provider_by_known_function() 0 16 1
A test_get_main_network_site_wpcom_id_multisite() 0 26 2
A test_sync_does_not_send_updates_if_array_order_is_only_change() 17 17 1
A reorder_array_keys() 0 10 1
A test_get_main_network_site_wpcom_id_single() 0 8 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WP_Test_Jetpack_Sync_Functions often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WP_Test_Jetpack_Sync_Functions, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
4
use Automattic\Jetpack\Blocks;
5
use Automattic\Jetpack\Constants;
6
use Automattic\Jetpack\Sync\Defaults;
7
use Automattic\Jetpack\Sync\Functions;
8
use Automattic\Jetpack\Sync\Modules;
9
use Automattic\Jetpack\Sync\Modules\Callables;
10
use Automattic\Jetpack\Sync\Modules\WP_Super_Cache;
11
use Automattic\Jetpack\Sync\Sender;
12
use Automattic\Jetpack\Sync\Settings;
13
14
15
require_once 'test_class.jetpack-sync-base.php';
16
17
function jetpack_foo_is_callable() {
18
	return 'bar';
19
}
20
21
/**
22
 * Testing Functions
23
 */
24
class WP_Test_Jetpack_Sync_Functions extends WP_Test_Jetpack_Sync_Base {
25
	protected $post;
26
	protected $callable_module;
27
28
	protected static $admin_id; // used in mock_xml_rpc_request
29
30
	public function setUp() {
31
		parent::setUp();
32
33
		$this->resetCallableAndConstantTimeouts();
34
35
		$this->callable_module = Modules::get_module( "functions" );
36
		set_current_screen( 'post-user' ); // this only works in is_admin()
37
	}
38
39 View Code Duplication
	function test_white_listed_function_is_synced() {
40
		$this->callable_module->set_callable_whitelist( array( 'jetpack_foo' => 'jetpack_foo_is_callable' ) );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Automattic\Jetpack\Sync\Modules\Module as the method set_callable_whitelist() does only exist in the following sub-classes of Automattic\Jetpack\Sync\Modules\Module: Automattic\Jetpack\Sync\Modules\Callables. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
41
42
		$this->sender->do_sync();
43
44
		$synced_value = $this->server_replica_storage->get_callable( 'jetpack_foo' );
45
		$this->assertEquals( jetpack_foo_is_callable(), $synced_value );
46
	}
47
48
	public function test_sync_jetpack_updates() {
49
		$this->sender->do_sync();
50
		$updates = $this->server_replica_storage->get_callable( 'updates' );
51
		$this->assertEqualsObject( Jetpack::get_updates(), $updates, 'The updates object should match' );
52
	}
53
54
55
	function test_wp_version_is_synced() {
56
		global $wp_version;
57
		$this->sender->do_sync();
58
		$synced_value = $this->server_replica_storage->get_callable( 'wp_version' );
59
		$this->assertEquals( $synced_value, $wp_version );
60
	}
61
62
	public function test_sync_callable_whitelist() {
63
		// $this->setSyncClientDefaults();
64
65
		add_filter( 'jetpack_set_available_extensions',  array( $this, 'add_test_block' ) );
66
		Jetpack_Gutenberg::init();
67
		Blocks::jetpack_register_block( 'jetpack/test' );
68
69
		$callables = array(
70
			'wp_max_upload_size'               => wp_max_upload_size(),
71
			'is_main_network'                  => Jetpack::is_multi_network(),
72
			'is_multi_site'                    => is_multisite(),
73
			'main_network_site'                => Functions::main_network_site_url(),
74
			'single_user_site'                 => Jetpack::is_single_user_site(),
75
			'updates'                          => Jetpack::get_updates(),
76
			'home_url'                         => Functions::home_url(),
77
			'site_url'                         => Functions::site_url(),
78
			'has_file_system_write_access'     => Functions::file_system_write_access(),
79
			'is_version_controlled'            => Functions::is_version_controlled(),
80
			'taxonomies'                       => Functions::get_taxonomies(),
81
			'post_types'                       => Functions::get_post_types(),
82
			'post_type_features'               => Functions::get_post_type_features(),
83
			'rest_api_allowed_post_types'      => Functions::rest_api_allowed_post_types(),
84
			'rest_api_allowed_public_metadata' => Functions::rest_api_allowed_public_metadata(),
85
			'sso_is_two_step_required'         => Jetpack_SSO_Helpers::is_two_step_required(),
86
			'sso_should_hide_login_form'       => Jetpack_SSO_Helpers::should_hide_login_form(),
87
			'sso_match_by_email'               => Jetpack_SSO_Helpers::match_by_email(),
88
			'sso_new_user_override'            => Jetpack_SSO_Helpers::new_user_override(),
89
			'sso_bypass_default_login_form'    => Jetpack_SSO_Helpers::bypass_login_forward_wpcom(),
90
			'wp_version'                       => Functions::wp_version(),
91
			'get_plugins'                      => Functions::get_plugins(),
92
			'get_plugins_action_links'         => Functions::get_plugins_action_links(),
93
			'active_modules'                   => Jetpack::get_active_modules(),
94
			'hosting_provider'                 => Functions::get_hosting_provider(),
95
			'locale'                           => get_locale(),
96
			'site_icon_url'                    => Functions::site_icon_url(),
97
			'shortcodes'                       => Functions::get_shortcodes(),
98
			'roles'                            => Functions::roles(),
99
			'timezone'                         => Functions::get_timezone(),
100
			'available_jetpack_blocks'         => Jetpack_Gutenberg::get_availability(),
101
			'paused_themes'                    => Functions::get_paused_themes(),
102
			'paused_plugins'                   => Functions::get_paused_plugins(),
103
			'main_network_site_wpcom_id'       => Functions::main_network_site_wpcom_id(),
104
			'theme_support'                    => Functions::get_theme_support(),
105
			'wp_get_environment_type'          => wp_get_environment_type(),
106
		);
107
108
		if ( function_exists( 'wp_cache_is_enabled' ) ) {
109
			$callables['wp_super_cache_globals'] = WP_Super_Cache::get_wp_super_cache_globals();
110
		}
111
112
		if ( is_multisite() ) {
113
			$callables['network_name']                        = Jetpack::network_name();
114
			$callables['network_allow_new_registrations']     = Jetpack::network_allow_new_registrations();
115
			$callables['network_add_new_users']               = Jetpack::network_add_new_users();
116
			$callables['network_site_upload_space']           = Jetpack::network_site_upload_space();
117
			$callables['network_upload_file_types']           = Jetpack::network_upload_file_types();
118
			$callables['network_enable_administration_menus'] = Jetpack::network_enable_administration_menus();
119
		}
120
121
		$this->sender->do_sync();
122
123
		foreach ( $callables as $name => $value ) {
124
			// TODO: figure out why _sometimes_ the 'support' value of
125
			// the post_types value is being removed from the output
126
			if ( $name === 'post_types' ) {
127
				continue;
128
			}
129
130
			$this->assertCallableIsSynced( $name, $value );
131
		}
132
133
		$whitelist_keys = array_keys( $this->callable_module->get_callable_whitelist() );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Automattic\Jetpack\Sync\Modules\Module as the method get_callable_whitelist() does only exist in the following sub-classes of Automattic\Jetpack\Sync\Modules\Module: Automattic\Jetpack\Sync\Modules\Callables. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
134
		$callables_keys = array_keys( $callables );
135
136
		// Are we testing all the callables in the defaults?
137
		$whitelist_and_callable_keys_difference = array_diff( $whitelist_keys, $callables_keys );
138
		$this->assertTrue( empty( $whitelist_and_callable_keys_difference ), 'Some whitelisted options don\'t have a test: ' . print_r( $whitelist_and_callable_keys_difference, 1 ) );
139
140
		// Are there any duplicate keys?
141
		$unique_whitelist = array_unique( $whitelist_keys );
142
		$this->assertEquals( count( $unique_whitelist ), count( $whitelist_keys ), 'The duplicate keys are: ' . print_r( array_diff_key( $whitelist_keys, array_unique( $whitelist_keys ) ), 1 ) );
143
144
		remove_filter( 'jetpack_set_available_extensions',  array( $this, 'add_test_block' ) );
145
		Jetpack_Gutenberg::reset();
146
	}
147
148
	public function add_test_block() {
149
		return array( 'test' );
150
	}
151
152
	function assertCallableIsSynced( $name, $value ) {
153
		$this->assertEqualsObject( $value, $this->server_replica_storage->get_callable( $name ), 'Function ' . $name . ' didn\'t have the expected value of ' . json_encode( $value ) );
154
	}
155
156
	function test_white_listed_callables_doesnt_get_synced_twice() {
157
		delete_transient( Callables::CALLABLES_AWAIT_TRANSIENT_NAME );
158
		delete_option( Callables::CALLABLES_CHECKSUM_OPTION_NAME );
159
		$this->callable_module->set_callable_whitelist( array( 'jetpack_foo' => 'jetpack_foo_is_callable' ) );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Automattic\Jetpack\Sync\Modules\Module as the method set_callable_whitelist() does only exist in the following sub-classes of Automattic\Jetpack\Sync\Modules\Module: Automattic\Jetpack\Sync\Modules\Callables. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
160
		$this->sender->do_sync();
161
162
		$synced_value = $this->server_replica_storage->get_callable( 'jetpack_foo' );
163
		$this->assertEquals( 'bar', $synced_value );
164
165
		$this->server_replica_storage->reset();
166
167
		delete_transient( Callables::CALLABLES_AWAIT_TRANSIENT_NAME );
168
		$this->sender->do_sync();
169
170
		$this->assertEquals( null, $this->server_replica_storage->get_callable( 'jetpack_foo' ) );
171
	}
172
173
	/**
174
	 * Tests that calling unlock_sync_callable_next_tick works as expected.
175
	 *
176
	 * Return null
177
	 */
178
	public function test_white_listed_callable_sync_on_next_tick() {
179
		// Setup...
180
		$this->callable_module->set_callable_whitelist( array( 'jetpack_foo' => 'jetpack_foo_is_callable_random' ) );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Automattic\Jetpack\Sync\Modules\Module as the method set_callable_whitelist() does only exist in the following sub-classes of Automattic\Jetpack\Sync\Modules\Module: Automattic\Jetpack\Sync\Modules\Callables. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
181
		$this->sender->do_sync();
182
		$initial_value = $this->server_replica_storage->get_callable( 'jetpack_foo' );
183
184
		// Action happends that should has the correct data only on the next page load.
185
		$this->callable_module->unlock_sync_callable_next_tick(); // Calling this should have no effect on this sync.
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Automattic\Jetpack\Sync\Modules\Module as the method unlock_sync_callable_next_tick() does only exist in the following sub-classes of Automattic\Jetpack\Sync\Modules\Module: Automattic\Jetpack\Sync\Modules\Callables. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
186
		$this->sender->do_sync();
187
		$should_be_initial_value = $this->server_replica_storage->get_callable( 'jetpack_foo' );
188
		$this->assertEquals( $initial_value, $should_be_initial_value );
189
190
		// Next tick...
191
		$this->sender->do_sync(); // This sync sends the updated data...
192
		$new_value = $this->server_replica_storage->get_callable( 'jetpack_foo' );
193
		$this->assertNotEquals( $initial_value, $new_value );
194
	}
195
196
	/**
197
	 * Tests that updating the theme should result in the no callabled transient being set.
198
	 *
199
	 * Return null
200
	 */
201
	public function test_updating_stylesheet_sends_the_theme_data() {
202
203
		// Make sure we don't already use this theme.
204
		$this->assertNotEquals( 'twentythirteen', get_option( 'stylesheet' ) );
205
206
		switch_theme( 'twentythirteen' );
207
		$this->sender->do_sync();
208
209
		// Since we can load up the data to see if new data will get send
210
		// this tests if we remove the transiant so that the data can get synced on the next tick.
211
		$this->assertFalse( get_transient( Callables::CALLABLES_AWAIT_TRANSIENT_NAME ) );
212
	}
213
214
	function test_sync_always_sync_changes_to_modules_right_away() {
215
		Jetpack::update_active_modules( array( 'stats' ) );
216
217
		$this->sender->do_sync();
218
219
		$synced_value = $this->server_replica_storage->get_callable( 'active_modules' );
220
		$this->assertEquals(  array( 'stats' ), $synced_value  );
221
222
		$this->server_replica_storage->reset();
223
224
		Jetpack::update_active_modules( array( 'json-api' ) );
225
		$this->sender->do_sync();
226
227
		$synced_value = $this->server_replica_storage->get_callable( 'active_modules' );
228
		$this->assertEquals( array( 'json-api' ), $synced_value );
229
	}
230
231
	function test_sync_always_sync_changes_to_home_siteurl_right_away() {
232
		$original_home_option    = get_option( 'home' );
233
		$original_siteurl_option = get_option( 'siteurl' );
234
235
		// Let's see if the original values get synced
236
		$this->sender->do_sync();
237
		$synced_home_url = $this->server_replica_storage->get_callable( 'home_url' );
238
		$synced_site_url = $this->server_replica_storage->get_callable( 'site_url' );
239
240
		$this->assertEquals( $original_home_option, $synced_home_url );
241
		$this->assertEquals( $original_siteurl_option, $synced_site_url );
242
243
		$this->server_replica_storage->reset();
244
245
		$updated_home_option    = 'http://syncrocks.com';
246
		$updated_siteurl_option = 'http://syncrocks.com';
247
248
		update_option( 'home', $updated_home_option );
249
		update_option( 'siteurl', $updated_siteurl_option );
250
251
		$this->sender->do_sync();
252
253
		$synced_home_url = $this->server_replica_storage->get_callable( 'home_url' );
254
		$synced_site_url = $this->server_replica_storage->get_callable( 'site_url' );
255
256
		$this->assertEquals( $updated_home_option, $synced_home_url );
257
		$this->assertEquals( $updated_siteurl_option, $synced_site_url );
258
259
		// Cleanup
260
		update_option( 'home', $original_home_option );
261
		update_option( 'siteurl', $original_siteurl_option );
262
	}
263
264
	function test_sync_jetpack_sync_unlock_sync_callable_action_allows_syncing_siteurl_changes() {
265
		$original_home_option    = get_option( 'home' );
266
		$original_siteurl_option = get_option( 'siteurl' );
267
268
		// Let's see if the original values get synced. This will also set the await transient.
269
		$this->sender->do_sync();
270
		$synced_home_url = $this->server_replica_storage->get_callable( 'home_url' );
271
		$synced_site_url = $this->server_replica_storage->get_callable( 'site_url' );
272
273
		$this->assertEquals( $original_home_option, $synced_home_url );
274
		$this->assertEquals( $original_siteurl_option, $synced_site_url );
275
276
		$this->server_replica_storage->reset();
277
278
		update_option( 'home', $this->return_https_site_com_blog() );
279
		update_option( 'siteurl', $this->return_https_site_com_blog() );
280
281
		/**
282
		 * Used to signal that the callables await transient should be cleared. Clearing the await transient is useful
283
		 * in cases where we need to sync values to WordPress.com sooner than the default wait time.
284
		 *
285
		 * @since 4.4.0
286
		 */
287
		do_action( 'jetpack_sync_unlock_sync_callable' );
288
289
		$_SERVER['HTTPS'] = 'on';
290
291
		$this->sender->do_sync();
292
293
		$synced_home_url = $this->server_replica_storage->get_callable( 'home_url' );
294
		$synced_site_url = $this->server_replica_storage->get_callable( 'site_url' );
295
296
		$this->assertEquals( $this->return_https_site_com_blog(), $synced_home_url );
297
		$this->assertEquals( $this->return_https_site_com_blog(), $synced_site_url );
298
299
		// Cleanup
300
		unset( $_SERVER['HTTPS'] );
301
302
		update_option( 'home', $original_home_option );
303
		update_option( 'siteurl', $original_siteurl_option );
304
	}
305
306
	function test_home_site_urls_synced_while_migrate_for_idc_set() {
307
		delete_transient( Callables::CALLABLES_AWAIT_TRANSIENT_NAME );
308
		delete_option( Callables::CALLABLES_CHECKSUM_OPTION_NAME );
309
310
		$home_option    = get_option( 'home' );
311
		$siteurl_option = get_option( 'siteurl' );
312
		$main_network   = network_site_url();
313
314
		// First, let's see if the original values get synced
315
		$this->sender->do_sync();
316
317
		$this->assertEquals( $home_option,  $this->server_replica_storage->get_callable( 'home_url' ) );
318
		$this->assertEquals( $siteurl_option, $this->server_replica_storage->get_callable( 'site_url' ) );
319
		$this->assertEquals( $main_network, $this->server_replica_storage->get_callable( 'main_network_site' ) );
320
321
		// Second, let's make sure that values don't get synced again if the migrate_for_idc option is not set
322
		$this->server_replica_storage->reset();
323
		delete_transient( Callables::CALLABLES_AWAIT_TRANSIENT_NAME );
324
		$this->sender->do_sync();
325
326
		$this->assertEquals( null, $this->server_replica_storage->get_callable( 'home_url' ) );
327
		$this->assertEquals( null, $this->server_replica_storage->get_callable( 'site_url' ) );
328
		$this->assertEquals( null, $this->server_replica_storage->get_callable( 'main_network_site' ) );
329
330
		// Third, let's test that values get syncd with the option set
331
		Jetpack_Options::update_option( 'migrate_for_idc', true );
332
333
		$this->server_replica_storage->reset();
334
		delete_transient( Callables::CALLABLES_AWAIT_TRANSIENT_NAME );
335
		$this->sender->do_sync();
336
337
		$this->assertEquals( $home_option,  $this->server_replica_storage->get_callable( 'home_url' ) );
338
		$this->assertEquals( $siteurl_option, $this->server_replica_storage->get_callable( 'site_url' ) );
339
		$this->assertEquals( $main_network, $this->server_replica_storage->get_callable( 'main_network_site' ) );
340
341
		Jetpack_Options::delete_option( 'migrate_for_idc' );
342
	}
343
344
	function return_example_com() {
345
		return 'http://example.com';
346
	}
347
348
	function return_example_com_blog() {
349
		return 'http://example.com/blog';
350
	}
351
352
	function return_https_example_com() {
353
		return 'https://example.com';
354
	}
355
356
	function return_https_example_org() {
357
		return 'https://example.org';
358
	}
359
360
	function return_site_com() {
361
		return 'http://site.com';
362
	}
363
364
	function return_https_site_com() {
365
		return 'https://site.com';
366
	}
367
368
	function return_https_site_com_blog() {
369
		return 'https://site.com/blog';
370
	}
371
372
	function return_https_www_example_com() {
373
		return 'https://www.example.com';
374
	}
375
376
	function return_https_foo_example_com() {
377
		return 'https://foo.example.com';
378
	}
379
380
	function test_get_protocol_normalized_url_works_with_no_history() {
381
		$callable_type = 'home_url';
382
		$option_key = Functions::HTTPS_CHECK_OPTION_PREFIX . $callable_type;
383
		delete_option( $option_key );
384
385
		$this->assertStringStartsWith(
386
			'http://',
387
			Functions::get_protocol_normalized_url( $callable_type, $this->return_example_com() )
388
		);
389
390
		delete_option( $option_key );
391
392
		$this->assertStringStartsWith(
393
			'https://',
394
			Functions::get_protocol_normalized_url( $callable_type, $this->return_https_example_com() )
395
		);
396
397
		$this->assertCount( 1, get_option( $option_key ) );
398
399
		delete_option( $option_key );
400
	}
401
402
	function test_get_protocol_normalized_url_stores_max_history() {
403
		$callable_type = 'home_url';
404
		$option_key = Functions::HTTPS_CHECK_OPTION_PREFIX . $callable_type;
405
		delete_option( $option_key );
406
		for ( $i = 0; $i < 20; $i++ ) {
407
			Functions::get_protocol_normalized_url( $callable_type, $this->return_example_com() );
408
		}
409
410
		$this->assertCount( Functions::HTTPS_CHECK_HISTORY, get_option( $option_key ) );
411
		delete_option( $option_key );
412
	}
413
414
	function test_get_protocol_normalized_url_returns_http_when_https_falls_off() {
415
		$callable_type = 'home_url';
416
		$option_key = Functions::HTTPS_CHECK_OPTION_PREFIX . $callable_type;
417
		delete_option( $option_key );
418
419
		// Start with one https scheme
420
		$this->assertStringStartsWith(
421
			'https://',
422
			Functions::get_protocol_normalized_url( $callable_type, $this->return_https_example_com() )
423
		);
424
425
		// Now add enough http schemes to fill up the history
426
		for ( $i = 1; $i < Functions::HTTPS_CHECK_HISTORY; $i++ ) {
427
			$this->assertStringStartsWith(
428
				'https://',
429
				Functions::get_protocol_normalized_url( $callable_type, $this->return_example_com() )
430
			);
431
		}
432
433
		// Now that the history is full, this one should cause the function to return false.
434
		$this->assertStringStartsWith(
435
			'http://',
436
			Functions::get_protocol_normalized_url( $callable_type, $this->return_example_com() )
437
		);
438
	}
439
440
	function test_get_protocol_normalized_url_returns_new_value_cannot_parse() {
441
		$test_url = 'http:///example.com';
442
		$this->assertEquals(
443
			$test_url,
444
			Functions::get_protocol_normalized_url( 'home_url', $test_url )
445
		);
446
	}
447
448
	function test_get_protocol_normalized_url_cleared_on_reset_data() {
449
		Functions::get_protocol_normalized_url( 'home_url', get_home_url() );
450
		Functions::get_protocol_normalized_url( 'site_url', get_site_url() );
451
		Functions::get_protocol_normalized_url( 'main_network_site_url', network_site_url() );
452
453
		$url_callables = array( 'home_url', 'site_url', 'main_network_site_url' );
454
		foreach( $url_callables as $callable ) {
455
			$this->assertInternalType( 'array', get_option( Functions::HTTPS_CHECK_OPTION_PREFIX . $callable) );
456
		}
457
458
		Sender::get_instance()->uninstall();
459
460
		foreach( $url_callables as $callable ) {
461
			$this->assertFalse( get_option( Functions::HTTPS_CHECK_OPTION_PREFIX . $callable ) );
462
		}
463
	}
464
465
	function test_subdomain_switching_to_www_does_not_cause_sync() {
466
		// a lot of sites accept www.domain.com or just domain.com, and we want to prevent lots of
467
		// switching back and forth, so we force the domain to be the one in the siteurl option
468
		$this->setSyncClientDefaults();
469
		delete_transient( Callables::CALLABLES_AWAIT_TRANSIENT_NAME );
470
		delete_option( Callables::CALLABLES_CHECKSUM_OPTION_NAME );
471
472
		$original_site_url = site_url();
473
474
		// sync original value
475
		$this->sender->do_sync();
476
477
		$this->assertEquals( $original_site_url, $this->server_replica_storage->get_callable( 'site_url' ) );
478
479
		add_filter( 'site_url', array( $this, 'add_www_subdomain_to_siteurl' ) );
480
481
		delete_transient( Callables::CALLABLES_AWAIT_TRANSIENT_NAME );
482
		delete_option( Callables::CALLABLES_CHECKSUM_OPTION_NAME );
483
		$this->sender->do_sync();
484
485
		$this->assertEquals( $original_site_url, $this->server_replica_storage->get_callable( 'site_url' ) );
486
	}
487
488 View Code Duplication
	function test_sync_limited_set_of_callables_if_cron() {
489
		$all_callables = array_keys( Defaults::get_callable_whitelist() );
490
		$always_updated = Callables::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS;
491
492
		foreach ( $always_updated as $key => $option ) {
493
			if ( array_key_exists( $option, Callables::OPTION_NAMES_TO_CALLABLE_NAMES ) ) {
494
				$always_updated[ $key ] = Callables::OPTION_NAMES_TO_CALLABLE_NAMES[ $option ];
495
			}
496
497
		}
498
499
		// non-admin
500
		set_current_screen( 'front' );
501
		Settings::set_doing_cron( true );
502
503
		$this->sender->do_sync();
504
505
		foreach ( $all_callables as $callable ) {
506
			if ( in_array( $callable, $always_updated, true ) ) {
507
				$this->assertNotNull( $this->server_replica_storage->get_callable( $callable ) );
508
			} else {
509
				$this->assertEquals( null, $this->server_replica_storage->get_callable( $callable ) );
510
			}
511
		}
512
513
		Settings::set_doing_cron( false );
514
	}
515
516 View Code Duplication
	function test_sync_limited_set_of_callables_if_wp_cli() {
517
		$all_callables = array_keys( Defaults::get_callable_whitelist() );
518
		$always_updated = Callables::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS;
519
520
		foreach ( $always_updated as $key => $option ) {
521
			if ( array_key_exists( $option, Callables::OPTION_NAMES_TO_CALLABLE_NAMES ) ) {
522
				$always_updated[ $key ] = Callables::OPTION_NAMES_TO_CALLABLE_NAMES[ $option ];
523
			}
524
525
		}
526
527
		// non-admin
528
		set_current_screen( 'front' );
529
		Constants::set_constant( 'WP_CLI', true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
530
531
		$this->sender->do_sync();
532
533
		foreach ( $all_callables as $callable ) {
534
			if ( in_array( $callable, $always_updated, true ) ) {
535
				$this->assertNotNull( $this->server_replica_storage->get_callable( $callable ) );
536
			} else {
537
				$this->assertEquals( null, $this->server_replica_storage->get_callable( $callable ) );
538
			}
539
		}
540
541
		Constants::set_constant( 'WP_CLI', false );
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
542
	}
543
544
	function test_site_icon_url_returns_false_when_no_site_icon() {
545
		delete_option( 'jetpack_site_icon_url' );
546
		$this->sender->do_sync();
547
		$this->assertFalse( $this->server_replica_storage->get_callable( 'site_icon_url' ) );
548
	}
549
550
	function test_site_icon_url_returns_core_site_icon_url_when_set() {
551
		$attachment_id = $this->factory->post->create( array(
552
			'post_type'      => 'attachment',
553
			'post_mime_type' => 'image/png',
554
		) );
555
		add_post_meta( $attachment_id, '_wp_attached_file', '2016/09/core_site_icon_url.png' );
556
		update_option( 'site_icon', $attachment_id );
557
		update_option( 'jetpack_site_icon_url', 'http://website.com/wp-content/uploads/2016/09/jetpack_site_icon.png' );
558
559
		$this->sender->do_sync();
560
561
		$this->assertContains( 'core_site_icon_url', $this->server_replica_storage->get_callable( 'site_icon_url' ) );
562
563
		delete_option( 'site_icon' );
564
	}
565
566
	function test_site_icon_url_fallback_to_jetpack_site_icon_url() {
567
		delete_option( 'site_icon' );
568
		update_option( 'jetpack_site_icon_url', 'http://website.com/wp-content/uploads/2016/09/jetpack_site_icon.png' );
569
		$this->sender->do_sync();
570
571
		$this->assertContains( 'jetpack_site_icon', $this->server_replica_storage->get_callable( 'site_icon_url' ) );
572
	}
573
574
	function test_calling_taxonomies_do_not_modify_global() {
575
		global $wp_taxonomies;
576
		// adds taxonomies.
577
		$test = new ABC_FOO_TEST_Taxonomy_Example();
0 ignored issues
show
Unused Code introduced by
$test is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
578
		$this->setSyncClientDefaults();
579
		$sync_callable_taxonomies = Functions::get_taxonomies();
580
581
		$this->assertNull( $sync_callable_taxonomies['example']->update_count_callback );
582
		$this->assertNull( $sync_callable_taxonomies['example']->meta_box_cb );
583
584
		$this->assertNotNull( $wp_taxonomies['example']->update_count_callback );
585
		$this->assertNotNull( $wp_taxonomies['example']->meta_box_cb );
586
587
	}
588
589
	function test_sanitize_sync_taxonomies_method() {
590
591
		$sanitized = Functions::sanitize_taxonomy( (object) array( 'meta_box_cb' => 'post_tags_meta_box' ) );
592
		$this->assertEquals( $sanitized->meta_box_cb, 'post_tags_meta_box' );
593
594
		$sanitized = Functions::sanitize_taxonomy( (object) array( 'meta_box_cb' => 'post_categories_meta_box' ) );
595
		$this->assertEquals( $sanitized->meta_box_cb, 'post_categories_meta_box' );
596
597
		$sanitized = Functions::sanitize_taxonomy( (object) array( 'meta_box_cb' => 'banana' ) );
598
		$this->assertEquals( $sanitized->meta_box_cb, null );
599
600
		$sanitized = Functions::sanitize_taxonomy( (object) array( 'update_count_callback' => 'banana' ) );
601
		$this->assertFalse( isset( $sanitized->update_count_callback ) );
602
603
		$sanitized = Functions::sanitize_taxonomy( (object) array( 'rest_controller_class' => 'banana' ) );
604
		$this->assertEquals( $sanitized->rest_controller_class, null );
605
606
		$sanitized = Functions::sanitize_taxonomy( (object) array( 'rest_controller_class' => 'WP_REST_Terms_Controller' ) );
607
608
		$this->assertEquals( $sanitized->rest_controller_class, 'WP_REST_Terms_Controller' );
609
	}
610
611 View Code Duplication
	function test_sanitize_sync_post_type_method_default() {
612
		$label = 'foo_default';
613
		$post_type_object = new WP_Post_Type( $label );
614
		$post_type_object->add_supports();
615
		$post_type_object->add_rewrite_rules();
616
		$post_type_object->register_meta_boxes();
617
		$post_type_object->add_hooks();
618
		$post_type_object->register_taxonomies();
619
620
		$sanitized = Functions::sanitize_post_type( $post_type_object );
621
		$this->assert_sanitized_post_type_default( $sanitized, $label );
622
623
	}
624
625 View Code Duplication
	function test_sanitize_sync_post_type_method_remove_unknown_values_set() {
626
		$label = 'foo_strange';
627
		$post_type_object = new WP_Post_Type( $label, array( 'foo' => 'bar' ) );
628
		$post_type_object->add_supports();
629
		$post_type_object->add_rewrite_rules();
630
		$post_type_object->register_meta_boxes();
631
		$post_type_object->add_hooks();
632
		$post_type_object->register_taxonomies();
633
634
		$sanitized = Functions::sanitize_post_type( $post_type_object );
635
		$this->assert_sanitized_post_type_default( $sanitized, $label );
636
	}
637
638
	function assert_sanitized_post_type_default( $sanitized, $label ) {
639
		$this->assertEquals( $label, $sanitized->name );
640
		$this->assertEquals( 'Posts', $sanitized->label );
641
		$this->assertEquals( '', $sanitized->description );
642
		$this->assertEquals( $label, $sanitized->rewrite['slug'] );
643
		$this->assertEquals( $label, $sanitized->query_var );
644
		$this->assertEquals( 'post', $sanitized->capability_type );
645
		$this->assertEquals( array(), $sanitized->taxonomies );
646
		$this->assertEquals( array(), $sanitized->supports );
647
		$this->assertEquals( '', $sanitized->_edit_link );
648
649
		$this->assertFalse( $sanitized->public );
650
		$this->assertFalse( $sanitized->has_archive );
651
		$this->assertFalse( $sanitized->publicly_queryable );
652
		$this->assertFalse( $sanitized->hierarchical );
653
		$this->assertFalse( $sanitized->show_ui );
654
		$this->assertFalse( $sanitized->show_in_menu );
655
		$this->assertFalse( $sanitized->show_in_nav_menus );
656
		$this->assertFalse( $sanitized->show_in_admin_bar );
657
		$this->assertFalse( $sanitized->rest_base );
658
		$this->assertFalse( $sanitized->_builtin );
659
660
		$this->assertTrue( $sanitized->exclude_from_search );
661
		$this->assertTrue( $sanitized->can_export );
662
		$this->assertTrue( $sanitized->map_meta_cap );
663
		$this->assertTrue( is_object( $sanitized->labels ) );
664
		$this->assertTrue( is_array( $sanitized->rewrite ) );
665
		$this->assertTrue( is_object( $sanitized->cap ) );
666
667
	}
668
669
	function test_sanitize_sync_post_type_method_all_values_set() {
670
		$args = array(
671
			'labels'                => array(
672
				'stuff' => 'apple',
673
			),
674
			'description'           => 'banana',
675
			'public'                => true,
676
			'hierarchical'          => true,
677
			'exclude_from_search'   => false,
678
			'publicly_queryable'    => true,
679
			'show_ui'               => true,
680
			'show_in_menu'          => true,
681
			'show_in_nav_menus'     => true,
682
			'show_in_admin_bar'     => true,
683
			'menu_position'         => 10,
684
			'menu_icon'             => 'jetpack',
685
			'capability_type'       => 'foo',
686
			'capabilities'          => array( 'banana' => true ),
687
			'map_meta_cap'          => false,
688
			'supports'              => array( 'everything' ),
689
			'taxonomies'            => array( 'orange'),
690
			'has_archive'           => true,
691
			'rewrite'               => false,
692
			'query_var'             => 'foo_all_stuff',
693
			'can_export'            => false,
694
			'delete_with_user'      => true,
695
			'show_in_rest'          => true,
696
			'rest_base'             => 'foo_all_stuffing',
697
		);
698
		$post_type_object = new WP_Post_Type( 'foo_all', $args );
699
		$post_type_object->add_supports();
700
		$post_type_object->add_rewrite_rules();
701
		$post_type_object->register_meta_boxes();
702
		$post_type_object->add_hooks();
703
		$post_type_object->register_taxonomies();
704
705
		$sanitized = Functions::sanitize_post_type( $post_type_object );
706
		foreach( $args as $arg_key => $arg_value ) {
707
			//
708
			if ( in_array( $arg_key, array( 'labels', 'capabilities', 'supports' ) ) ) {
709
				continue;
710
			}
711
			$this->assertEquals( $arg_value, $sanitized->{ $arg_key }, 'Value for ' . $arg_key . 'not as expected' );
712
		}
713
	}
714
715
	function test_get_post_types_method() {
716
		global $wp_post_types;
717
		$synced = Functions::get_post_types();
718
		foreach( $wp_post_types as $post_type => $post_type_object ) {
719
			$post_type_object->rest_controller_class = false;
720
			if ( ! isset( $post_type_object->supports ) ) {
721
				$post_type_object->supports = array();
722
			}
723
			$synced_post_type = Functions::expand_synced_post_type( $synced[ $post_type ], $post_type );
724
			$this->assertEqualsObject( $post_type_object, $synced_post_type, 'POST TYPE :'. $post_type . ' not equal' );
725
		}
726
	}
727
728
	function test_register_post_types_callback_error() {
729
		register_post_type( 'testing', array( 'register_meta_box_cb' => function() {} ) );
730
		$this->sender->do_sync();
731
732
		$post_types =  $this->server_replica_storage->get_callable( 'post_types' );
733
		$this->assertTrue( isset( $post_types['testing'] ) );
734
	}
735
736
	function test_get_raw_url_by_option_bypasses_filters() {
737
		add_filter( 'option_home', array( $this, 'return_filtered_url' ) );
738
		$this->assertTrue( 'http://filteredurl.com' !== Functions::get_raw_url( 'home' ) );
739
		remove_filter( 'option_home', array( $this, 'return_filtered_url' ) );
740
	}
741
742
	function test_get_raw_url_by_constant_bypasses_filters() {
743
		Constants::set_constant( 'WP_HOME', 'http://constanturl.com' );
744
		Constants::set_constant( 'WP_SITEURL', 'http://constanturl.com' );
745
		add_filter( 'option_home', array( $this, 'return_filtered_url' ) );
746
		add_filter( 'option_siteurl', array( $this, 'return_filtered_url' ) );
747
748
		if ( is_multisite() ) {
749
			$this->assertTrue( $this->return_filtered_url() !== Functions::get_raw_url( 'home' ) );
750
			$this->assertTrue( $this->return_filtered_url() !== Functions::get_raw_url( 'siteurl' ) );
751
		} else {
752
			$this->assertEquals( 'http://constanturl.com', Functions::get_raw_url( 'home' ) );
753
			$this->assertEquals( 'http://constanturl.com', Functions::get_raw_url( 'siteurl' ) );
754
		}
755
756
		remove_filter( 'option_home', array( $this, 'return_filtered_url' ) );
757
		remove_filter( 'option_siteurl', array( $this, 'return_filtered_url' ) );
758
		Constants::clear_constants();
759
	}
760
761 View Code Duplication
	function test_get_raw_url_returns_with_http_if_is_ssl() {
762
		$home_option = get_option( 'home' );
763
764
		// Test without https first
765
		$this->assertEquals( $home_option, Functions::get_raw_url( 'home' ) );
766
767
		// Now, with https
768
		$_SERVER['HTTPS'] = 'on';
769
		$this->assertEquals(
770
			set_url_scheme( $home_option, 'http' ),
771
			Functions::get_raw_url( 'home' )
772
		);
773
		unset( $_SERVER['HTTPS'] );
774
	}
775
776 View Code Duplication
	function test_raw_home_url_is_https_when_is_ssl() {
777
		Constants::set_constant( 'JETPACK_SYNC_USE_RAW_URL', true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
778
779
		$home_option = get_option( 'home' );
780
781
		// Test without https first
782
		$this->assertEquals(
783
			$home_option,
784
			Functions::home_url()
785
		);
786
787
		// Now, with https
788
		$_SERVER['HTTPS'] = 'on';
789
		$this->assertEquals(
790
			set_url_scheme( $home_option, 'https' ),
791
			Functions::home_url()
792
		);
793
		unset( $_SERVER['HTTPS'] );
794
	}
795
796
	function test_user_can_stop_raw_urls() {
797
		add_filter( 'option_home', array( $this, 'return_filtered_url' ) );
798
		add_filter( 'option_siteurl', array( $this, 'return_filtered_url' ) );
799
800
		// Test with constant first
801
		$this->assertTrue( 'http://filteredurl.com' !== Functions::home_url() );
802
803
		// Now, without, which should return the filtered URL
804
		Constants::set_constant( 'JETPACK_SYNC_USE_RAW_URL', false );
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
805
		$this->assertEquals( $this->return_filtered_url(), Functions::home_url() );
806
		Constants::clear_constants();
807
808
		remove_filter( 'option_home', array( $this, 'return_filtered_url' ) );
809
		remove_filter( 'option_siteurl', array( $this, 'return_filtered_url' ) );
810
	}
811
812
	function test_plugin_action_links_get_synced() {
813
		// Makes sure that we start fresh
814
		delete_transient( 'jetpack_plugin_api_action_links_refresh' );
815
		$helper_all = new Jetpack_Sync_Test_Helper();
816
817
		$helper_all->array_override = array( '<a href="fun.php">fun</a>' );
818
		add_filter( 'plugin_action_links', array( $helper_all, 'filter_override_array' ), 10 );
819
820
		$helper_jetpack = new Jetpack_Sync_Test_Helper();
821
		$helper_jetpack->array_override = array( '<a href="settings.php">settings</a>', '<a href="https://jetpack.com/support">support</a>' );
822
		add_filter( 'plugin_action_links_jetpack/jetpack.php', array( $helper_jetpack, 'filter_override_array' ), 10 );
823
824
		set_current_screen( 'banana' );
825
		// Let's see if the original values get synced
826
		$this->sender->do_sync();
827
828
		$plugins_action_links = $this->server_replica_storage->get_callable( 'get_plugins_action_links' );
829
830
		$expected_array = array(
831
			'hello.php' => array(
832
				'fun' => admin_url( 'fun.php' )
833
			),
834
			'jetpack/jetpack.php' => array(
835
				'settings' => admin_url( 'settings.php' ),
836
				'support' => 'https://jetpack.com/support'
837
			)
838
		);
839
840
		$this->assertEquals( $expected_array, $this->extract_plugins_we_are_testing( $plugins_action_links )  );
841
842
		$helper_all->array_override = array( '<a href="not-fun.php">not fun</a>' );
843
844
		$this->resetCallableAndConstantTimeouts();
845
846
		set_current_screen( 'banana' );
847
		$this->sender->do_sync();
848
849
		$plugins_action_links = $this->server_replica_storage->get_callable( 'get_plugins_action_links' );
850
851
		// Nothing should have changed since we cache the results.
852
		$this->assertEquals( $this->extract_plugins_we_are_testing( $plugins_action_links ), $expected_array );
853
854
		if ( file_exists( WP_PLUGIN_DIR . '/hello.php' ) ) {
855
			activate_plugin('hello.php', '', false, true );
856
		}
857
		if ( file_exists( WP_PLUGIN_DIR . '/hello-dolly/hello.php' ) ) {
858
			activate_plugin('hello-dolly/hello.php', '', false, true );
859
		}
860
861
		$this->resetCallableAndConstantTimeouts();
862
		set_current_screen( 'banana' );
863
		$this->sender->do_sync();
864
865
		$plugins_action_links = $this->server_replica_storage->get_callable( 'get_plugins_action_links' );
866
867
		// Links should have changes now since we activated the plugin.
868
		$expected_array['hello.php'] = array( 'not fun' => admin_url( 'not-fun.php' ) );
869
		$this->assertEquals( $this->extract_plugins_we_are_testing( $plugins_action_links ), $expected_array, 'Array was not updated to the new value as expected' );
870
	}
871
872
	function extract_plugins_we_are_testing( $plugins_action_links ) {
873
		$only_plugins_we_care_about = array();
874
		if ( isset( $plugins_action_links['hello.php'] ) ) {
875
			$only_plugins_we_care_about['hello.php'] = isset( $plugins_action_links['hello.php'] ) ? $plugins_action_links['hello.php'] : '';
876
		} else {
877
			$only_plugins_we_care_about['hello.php'] = isset( $plugins_action_links['hello-dolly/hello.php'] ) ? $plugins_action_links['hello-dolly/hello.php'] : '';
878
		}
879
880
		$only_plugins_we_care_about['jetpack/jetpack.php'] = isset( $plugins_action_links['jetpack/jetpack.php'] ) ? $plugins_action_links['jetpack/jetpack.php'] : '';
881
		return $only_plugins_we_care_about;
882
	}
883
884
	function cause_fatal_error( $actions ) {
885
		unset( $actions['activate'] );
886
		$actions[] = '<a href="/hello">world</a>';
887
		return $actions;
888
	}
889
890
	function test_fixes_fatal_error( ) {
891
892
		delete_transient( 'jetpack_plugin_api_action_links_refresh' );
893
		add_filter( 'plugin_action_links', array( $this, 'cause_fatal_error' ) );
894
895
		set_current_screen( 'plugins' );
896
897
		$this->resetCallableAndConstantTimeouts();
898
		set_current_screen( 'plugins' );
899
		$this->sender->do_sync();
900
		$plugins_action_links = $this->server_replica_storage->get_callable( 'get_plugins_action_links' );
901
		$plugins_action_links = $this->extract_plugins_we_are_testing( $plugins_action_links );
902
		$this->assertTrue( isset( $plugins_action_links['hello.php']['world'] ), 'World is not set' );
903
	}
904
905
	/**
906
	 * Return "http://filteredurl.com".
907
	 *
908
	 * @return string
909
	 */
910
	public function return_filtered_url() {
911
		return 'http://filteredurl.com';
912
	}
913
914
	/**
915
	 * Add a "www" subdomain to a URL.
916
	 *
917
	 * @param string $url URL.
918
	 * @return string
919
	 */
920
	public function add_www_subdomain_to_siteurl( $url ) {
921
		$parsed_url = wp_parse_url( $url );
922
923
		return "{$parsed_url['scheme']}://www.{$parsed_url['host']}";
924
	}
925
926
	/**
927
	 * Test "taxonomies_objects_do_not_have_meta_box_callback".
928
	 */
929
	public function test_taxonomies_objects_do_not_have_meta_box_callback() {
930
931
		new ABC_FOO_TEST_Taxonomy_Example();
932
		$taxonomies = Functions::get_taxonomies();
933
		$taxonomy   = $taxonomies['example'];
934
935
		$this->assertInternalType( 'object', $taxonomy );
936
		// Did we get rid of the expected attributes?
937
		$this->assertNull( $taxonomy->update_count_callback, 'example has the update_count_callback attribute, which should be removed since it is a callback' );
938
		$this->assertNull( $taxonomy->meta_box_cb, 'example has the meta_box_cb attribute, which should be removed since it is a callback' );
939
		$this->assertNull( $taxonomy->rest_controller_class );
940
		// Did we preserve the expected attributes?
941
		$check_object_vars = array(
942
			'labels',
943
			'description',
944
			'public',
945
			'publicly_queryable',
946
			'hierarchical',
947
			'show_ui',
948
			'show_in_menu',
949
			'show_in_nav_menus',
950
			'show_tagcloud',
951
			'show_in_quick_edit',
952
			'show_admin_column',
953
			'rewrite',
954
		);
955
		foreach ( $check_object_vars as $test ) {
956
			$this->assertObjectHasAttribute( $test, $taxonomy, "Taxonomy does not have expected {$test} attribute." );
957
		}
958
	}
959
960
	/**
961
	 * Test "force_sync_callable_on_plugin_update".
962
	 */
963
	public function test_force_sync_callable_on_plugin_update() {
964
		// fake the cron so that we really prevent the callables from being called.
965
		Settings::$is_doing_cron = true;
966
967
		$this->callable_module->set_callable_whitelist( array( 'jetpack_foo' => 'jetpack_foo_is_callable_random' ) );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Automattic\Jetpack\Sync\Modules\Module as the method set_callable_whitelist() does only exist in the following sub-classes of Automattic\Jetpack\Sync\Modules\Module: Automattic\Jetpack\Sync\Modules\Callables. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
968
		$this->sender->do_sync();
969
		$this->server_replica_storage->get_callable( 'jetpack_foo' );
970
971
		$this->server_replica_storage->reset();
972
973
		$synced_value2 = $this->server_replica_storage->get_callable( 'jetpack_foo' );
974
		$this->assertEmpty( $synced_value2 );
975
976
		$upgrader = (object) array(
977
			'skin' => (object) array(
978
				'result' => new WP_Error( 'fail', 'Fail' ),
979
			),
980
		);
981
982
		do_action(
983
			'upgrader_process_complete',
984
			$upgrader,
985
			array(
986
				'action'  => 'update',
987
				'type'    => 'plugin',
988
				'bulk'    => true,
989
				'plugins' => array( 'the/the.php' ),
990
			)
991
		);
992
993
		$this->sender->do_sync();
994
		$synced_value3           = $this->server_replica_storage->get_callable( 'jetpack_foo' );
995
		Settings::$is_doing_cron = false;
996
		$this->assertNotEmpty( $synced_value3, 'value is empty!' );
997
998
	}
999
1000
	/**
1001
	 * Test "xml_rpc_request_callables_has_actor".
1002
	 */
1003
	public function test_xml_rpc_request_callables_has_actor() {
1004
		$this->server_event_storage->reset();
1005
		$user = wp_get_current_user();
1006
		wp_set_current_user( 0 ); //
1007
		$this->sender->do_sync();
1008
		$event = $this->server_event_storage->get_most_recent_event( 'jetpack_sync_callable' );
1009
		$this->assertEquals( $event->user_id, 0, ' Callables user_id is null' );
1010
1011
		$this->resetCallableAndConstantTimeouts();
1012
		$this->mock_authenticated_xml_rpc(); // mock requet
1013
		$this->sender->do_sync();
1014
1015
		$event = $this->server_event_storage->get_most_recent_event( 'jetpack_sync_callable' );
1016
		// clean up by unsetting globals, etc. set previously by $this->mock_authenticated_xml_rpc()
1017
		$this->mock_authenticated_xml_rpc_cleanup( $user->ID );
1018
1019
		$this->assertEquals( $event->user_id, self::$admin_id, ' Callables XMLRPC_Reqeust not equal to event user_id' );
1020
	}
1021
1022
	/**
1023
	 * Mock authenticated XML RPC.
1024
	 */
1025
	public function mock_authenticated_xml_rpc() {
1026
		self::$admin_id = $this->factory->user->create( array(
1027
			'role' => 'administrator',
1028
		) );
1029
1030
		add_filter( 'pre_option_jetpack_private_options', array( $this, 'mock_jetpack_private_options' ), 10, 2 );
1031
		$_GET['token']     = 'pretend_this_is_valid:1:' . self::$admin_id;
1032
		$_GET['timestamp'] = (string) time();
1033
		$_GET['nonce']     = 'testing123';
1034
1035
		$_SERVER['REQUEST_URI']        = '/xmlrpc.php';
1036
		$_GET['body']                  = 'abc';
1037
		$_GET['body-hash']             = base64_encode( sha1( 'abc', true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
1038
		$GLOBALS['HTTP_RAW_POST_DATA'] = 'abc'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
1039
		$_SERVER['REQUEST_METHOD']     = 'POST';
1040
1041
		$normalized_request_pieces = array(
1042
			$_GET['token'],
1043
			$_GET['timestamp'],
1044
			$_GET['nonce'], // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1045
			$_GET['body-hash'],
1046
			'POST',
1047
			'example.org',
1048
			'80',
1049
			'/xmlrpc.php',
1050
		);
1051
		$normalize                 = join( "\n", $normalized_request_pieces ) . "\n";
1052
1053
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
1054
		$_GET['signature'] = base64_encode( hash_hmac( 'sha1', $normalize, 'secret', true ) );
1055
1056
		// call one of the authenticated endpoints
1057
		Constants::set_constant( 'XMLRPC_REQUEST', true );
0 ignored issues
show
Documentation introduced by
true is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1058
		Jetpack::init();
1059
		$connection = Jetpack::connection();
1060
		$connection->xmlrpc_methods( array() );
1061
		$connection->require_jetpack_authentication();
1062
		$connection->verify_xml_rpc_signature();
1063
	}
1064
1065
	/**
1066
	 * Mock authenticated XML RPC cleanup.
1067
	 *
1068
	 * @param int $user_id User ID.
1069
	 */
1070
	public function mock_authenticated_xml_rpc_cleanup( $user_id ) {
1071
		Constants::clear_constants();
1072
		remove_filter( 'pre_option_jetpack_private_options', array( $this, 'mock_jetpack_private_options' ), 10 );
1073
1074
		// phpcs:disable WordPress.Security.NonceVerification.Recommended
1075
		unset( $_GET['token'] );
1076
		unset( $_GET['timestamp'] );
1077
		unset( $_GET['nonce'] );
1078
		$_SERVER['REQUEST_URI'] = '';
1079
		unset( $_GET['body'] );
1080
		unset( $_GET['body-hash'] );
1081
		unset( $GLOBALS['HTTP_RAW_POST_DATA'] );
1082
		unset( $_SERVER['REQUEST_METHOD'] );
1083
		// phpcs:enable WordPress.Security.NonceVerification.Recommended
1084
1085
		Connection_Rest_Authentication::init()->reset_saved_auth_state();
1086
		Jetpack::connection()->reset_raw_post_data();
1087
		wp_set_current_user( $user_id );
1088
		self::$admin_id = null;
1089
	}
1090
1091
	/**
1092
	 * Mock Jetpack private options.
1093
	 */
1094 View Code Duplication
	public function mock_jetpack_private_options() {
1095
		$user_tokens                    = array();
1096
		$user_tokens[ self::$admin_id ] = 'pretend_this_is_valid.secret.' . self::$admin_id;
1097
		return array(
1098
			'user_tokens' => $user_tokens,
1099
		);
1100
	}
1101
1102
	/**
1103
	 * Test "get_timezone_from_timezone_string".
1104
	 */
1105
	public function test_get_timezone_from_timezone_string() {
1106
		update_option( 'timezone_string', 'America/Rankin_Inlet' );
1107
		update_option( 'gmt_offset', '' );
1108
		$this->assertEquals( 'America/Rankin Inlet', Functions::get_timezone() );
1109
	}
1110
1111
	/**
1112
	 * Test "get_timezone_from_gmt_offset_zero".
1113
	 */
1114
	public function test_get_timezone_from_gmt_offset_zero() {
1115
		update_option( 'timezone_string', '' );
1116
		update_option( 'gmt_offset', '0' );
1117
		$this->assertEquals( 'UTC+0', Functions::get_timezone() );
1118
	}
1119
1120
	/**
1121
	 * Test "get_timezone_from_gmt_offset_plus".
1122
	 */
1123
	public function test_get_timezone_from_gmt_offset_plus() {
1124
		update_option( 'timezone_string', '' );
1125
		update_option( 'gmt_offset', '1' );
1126
		$this->assertEquals( 'UTC+1', Functions::get_timezone() );
1127
	}
1128
1129
	/**
1130
	 * Test "get_timezone_from_gmt_offset_fractions".
1131
	 */
1132
	public function test_get_timezone_from_gmt_offset_fractions() {
1133
		update_option( 'timezone_string', '' );
1134
		update_option( 'gmt_offset', '5.5' );
1135
		$this->assertEquals( 'UTC+5:30', Functions::get_timezone() );
1136
	}
1137
1138
	/**
1139
	 * Test "get_timezone_from_gmt_offset_minus".
1140
	 */
1141
	public function test_get_timezone_from_gmt_offset_minus() {
1142
		update_option( 'timezone_string', '' );
1143
		update_option( 'gmt_offset', '-1' );
1144
		$this->assertEquals( 'UTC-1', Functions::get_timezone() );
1145
	}
1146
1147
	/**
1148
	 * Test "sync_callable_recursive_gets_checksum".
1149
	 */
1150 View Code Duplication
	public function test_sync_callable_recursive_gets_checksum() {
1151
1152
		$this->callable_module->set_callable_whitelist( array( 'jetpack_banana' => 'jetpack_recursive_banana' ) );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class Automattic\Jetpack\Sync\Modules\Module as the method set_callable_whitelist() does only exist in the following sub-classes of Automattic\Jetpack\Sync\Modules\Module: Automattic\Jetpack\Sync\Modules\Callables. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1153
		$this->sender->do_sync();
1154
		$synced_value = $this->server_replica_storage->get_callable( 'jetpack_banana' );
1155
		$this->assertTrue( ! empty( $synced_value ), 'We couldn\'t synced a value!' );
1156
	}
1157
1158
	/**
1159
	 * Test get_hosting_provider() callable to ensure that known hosts have the
1160
	 * right hosting provider returned.
1161
	 *
1162
	 * @return void
1163
	 */
1164
	public function test_get_hosting_provider_callable_with_unknown_host() {
1165
		$this->assertEquals( Functions::get_hosting_provider(), 'unknown' );
1166
	}
1167
1168
	/**
1169
	 * Test getting a hosting provider by a known constant
1170
	 *
1171
	 * @return void
1172
	 */
1173
	public function test_get_hosting_provider_by_known_constant() {
1174
		$functions = new Functions();
1175
		Constants::set_constant( 'GD_SYSTEM_PLUGIN_DIR', 'set' );
1176
		$this->assertEquals( $functions->get_hosting_provider_by_known_constant(), 'gd-managed-wp' );
1177
		Constants::clear_constants();
1178
1179
		Constants::set_constant( 'UNKNOWN', 'set' );
1180
		$this->assertFalse( $functions->get_hosting_provider_by_known_constant() );
1181
		Constants::clear_constants();
1182
	}
1183
1184
	/**
1185
	 * Test getting a hosting provider by a known class
1186
	 *
1187
	 * @return void
1188
	 */
1189
	public function test_get_hosting_provider_by_known_class() {
1190
		$functions = new Functions();
1191
1192
		$this->assertFalse( $functions->get_hosting_provider_by_known_class() );
1193
1194
		$class_mock = $this->getMockBuilder( '\\WPaaS\\Plugin' )
0 ignored issues
show
Unused Code introduced by
$class_mock is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1195
					->getMock(); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
1196
1197
		$this->assertEquals( $functions->get_hosting_provider_by_known_class(), 'gd-managed-wp' );
1198
1199
	}
1200
1201
	/**
1202
	 * Test getting a hosting provider by a known function
1203
	 *
1204
	 * @return bool
1205
	 */
1206
	public function test_get_hosting_provider_by_known_function() {
1207
1208
		/**
1209
		 * Stub is_wpe for testing function exists
1210
		 *
1211
		 * @return boolean
1212
		 */
1213
		function is_wpe() { // phpcs:ignore MediaWiki.Usage.NestedFunctions.NestedFunction
1214
			return true;
1215
		}
1216
1217
		$functions = new Functions();
1218
1219
		// Get hosting provider by known function.
1220
		$this->assertEquals( $functions->get_hosting_provider_by_known_function(), 'wpe' );
1221
	}
1222
1223
	/**
1224
	 * Test getting the main network site wpcom ID in multisite installs
1225
	 *
1226
	 * @return void
1227
	 */
1228
	public function test_get_main_network_site_wpcom_id_multisite() {
1229
		if ( ! is_multisite() ) {
1230
			$this->markTestSkipped( 'Only used on multisite' );
1231
		}
1232
1233
		// set the Jetpack ID for this site.
1234
		$main_network_wpcom_id = 12345;
1235
		\Jetpack_Options::update_option( 'id', $main_network_wpcom_id );
1236
1237
		$user_id = $this->factory->user->create();
1238
1239
		// NOTE this is necessary because WPMU causes certain assumptions about transients.
1240
		// to be wrong, and tests to explode. @see: https://github.com/sheabunge/WordPress/commit/ff4f1bb17095c6af8a0f35ac304f79074f3c3ff6 .
1241
		global $wpdb;
1242
1243
		$suppress      = $wpdb->suppress_errors();
1244
		$other_blog_id = wpmu_create_blog( 'foo.com', '', 'My Blog', $user_id );
1245
		$wpdb->suppress_errors( $suppress );
1246
1247
		switch_to_blog( $other_blog_id );
1248
1249
		$functions = new Functions();
1250
		$this->assertEquals( $main_network_wpcom_id, $functions->main_network_site_wpcom_id() );
1251
1252
		restore_current_blog();
1253
	}
1254
1255
	/**
1256
	 * Verify get_check_sum is consistent for differently ordered arrays.
1257
	 */
1258 View Code Duplication
	public function test_sync_does_not_send_updates_if_array_order_is_only_change() {
1259
		$plugins = Functions::get_plugins();
1260
1261
		// Let's see if the original values get synced.
1262
		$this->sender->do_sync();
1263
		$plugins_synced = $this->server_replica_storage->get_callable( 'get_plugins' );
1264
1265
		$this->assertEquals( $plugins, $plugins_synced );
1266
1267
		add_filter( 'all_plugins', array( $this, 'reorder_array_keys' ), 100, 1 );
1268
		do_action( 'jetpack_sync_unlock_sync_callable' );
1269
		$this->server_event_storage->reset();
1270
		$this->sender->do_sync();
1271
1272
		$event = $this->server_event_storage->get_most_recent_event( 'jetpack_sync_callable' );
1273
		$this->assertFalse( $event );
1274
	}
1275
1276
	/**
1277
	 * Reorder the get_plugins array keys.
1278
	 *
1279
	 * @param array $plugins array of plugins.
1280
	 *
1281
	 * @return array
1282
	 */
1283
	public function reorder_array_keys( $plugins ) {
1284
		// First plugin in array.
1285
		$plugin_key = array_keys( $plugins )[0];
1286
1287
		// reverse the 1st plugin's array entries.
1288
		$plugins[ $plugin_key ] = array_reverse( $plugins[ $plugin_key ] );
1289
1290
		// reverse the full array.
1291
		return array_reverse( $plugins );
1292
	}
1293
1294
	/**
1295
	 * Test getting the main network site wpcom ID in single site installs
1296
	 *
1297
	 * @return void
1298
	 */
1299
	public function test_get_main_network_site_wpcom_id_single() {
1300
		// set the Jetpack ID for this site.
1301
		$main_network_wpcom_id = 7891011;
1302
		\Jetpack_Options::update_option( 'id', $main_network_wpcom_id );
1303
1304
		$functions = new Functions();
1305
		$this->assertEquals( $main_network_wpcom_id, $functions->main_network_site_wpcom_id() );
1306
	}
1307
1308
}
1309
1310
/**
1311
 * Create a recursive object.
1312
 *
1313
 * @return object
1314
 */
1315
function jetpack_recursive_banana() {
1316
	$banana        = new stdClass();
1317
	$banana->arr   = array();
1318
	$banana->arr[] = $banana;
1319
	return $banana;
1320
}
1321
1322
/**
1323
 * Return a "random" number.
1324
 *
1325
 * Previously just returned `rand()`. I'm guessing something is trying to test
1326
 * caching or cache busting by having a different value returned each time, so
1327
 * let's do that reliably.
1328
 *
1329
 * @return int
1330
 */
1331
function jetpack_foo_is_callable_random() {
1332
	static $value = null;
1333
1334
	if ( null === $value ) {
1335
		$value = wp_rand();
1336
	}
1337
1338
	return $value++;
1339
}
1340
1341
// phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound
1342
/**
1343
 * Example Test Taxonomy
1344
 */
1345
class ABC_FOO_TEST_Taxonomy_Example {
1346
1347
	/**
1348
	 * Constructor. Duh.
1349
	 */
1350
	public function __construct() {
1351
1352
		register_taxonomy(
1353
			'example',
1354
			'posts',
1355
			array(
1356
				'meta_box_cb'           => 'bob',
1357
				'update_count_callback' => array( $this, 'callback_update_count_callback_tags' ), // phpcs:ignore WordPress.Arrays.CommaAfterArrayItem.NoComma
1358
				'rest_controller_class' => 'tom',
1359
			)
1360
		);
1361
	}
1362
1363
	/**
1364
	 * `update_count_callback` callback.
1365
	 *
1366
	 * @return int
1367
	 */
1368
	public function callback_update_count_callback_tags() {
1369
		return 123;
1370
	}
1371
1372
	/**
1373
	 * Prevent this class being used as part of a Serialization injection attack
1374
	 */
1375
	public function __clone() {
1376
		wp_die( 'Please don\'t __clone ' . __CLASS__ );
1377
	}
1378
1379
	/**
1380
	 * Prevent this class being used as part of a Serialization injection attack
1381
	 */
1382
	public function __wakeup() {
1383
		wp_die( 'Please don\'t __wakeup ' . __CLASS__ );
1384
	}
1385
}
1386