diff --git a/.travis.yml b/.travis.yml
index 8196d39..e6c8ec7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,36 +4,27 @@ cache:
   apt: true
   directories:
     - node_modules
+    - vendor
+    - $HOME/.composer/cache
 env:
   global:
     - WP_TRAVISCI=travis:phpunit
 matrix:
   include:
-  - php: 7.0
+  - php: 7.1
     env: WP_TRAVISCI=travis:js
-  - php: 5.2
-  - php: 5.3
-  - php: 5.4
-  - php: 5.5
+  - php: 7.1
+  - php: 7.0
   - php: 5.6
   - php: 5.6
     env: WP_TRAVIS_OBJECT_CACHE=true
     services: memcached
-  - php: hhvm
-    sudo: required
-    dist: trusty
-    group: edge
-    addons:
-      apt:
-        packages:
-        - mysql-server-5.6
-        - mysql-client-core-5.6
-        - mysql-client-5.6
-  - php: 7.0
-  - php: 7.1
+  - php: 5.5
+  - php: 5.4
+  - php: 5.3
+  - php: 5.2
   - php: nightly
   allow_failures:
-  - php: hhvm
   - php: nightly
 before_install:
 - |
@@ -43,16 +34,16 @@ before_install:
       sed -i "s/youremptytestdbnamehere/wordpress_tests/" wp-tests-config.php
       sed -i "s/yourusernamehere/root/" wp-tests-config.php
       sed -i "s/yourpasswordhere//" wp-tests-config.php
-      svn checkout https://plugins.svn.wordpress.org/wordpress-importer/trunk tests/phpunit/data/plugins/wordpress-importer
+      svn checkout https://plugins.svn.wordpress.org/wordpress-importer/tags/0.6.3/ tests/phpunit/data/plugins/wordpress-importer
   fi
 - |
   if [[ "$WP_TRAVIS_OBJECT_CACHE" == "true" ]]; then
-    curl https://raw.githubusercontent.com/tollmanz/wordpress-pecl-memcached-object-cache/584392b56dc4adbe52bd2c7b86f875e23a3e5f75/object-cache.php > src/wp-content/object-cache.php
+    cp tests/phpunit/includes/object-cache.php src/wp-content/object-cache.php
     echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
   fi
 before_script:
 - |
-  # Remove Xdebug for a huge performance increase, but not from nightly or hhvm:
+  # Remove Xdebug for a huge performance increase, but not from nightly:
   stable='^[0-9\.]+$'
   if [[ "$TRAVIS_PHP_VERSION" =~ $stable ]]; then
     phpenv config-rm xdebug.ini
@@ -67,9 +58,9 @@ before_script:
   # Install the specified version of PHPUnit depending on the PHP version:
   if [[ "$WP_TRAVISCI" == "travis:phpunit" ]]; then
     case "$TRAVIS_PHP_VERSION" in
-      7.1|7.0|hhvm|nightly)
-        echo "Using PHPUnit 5.7"
-        composer global require "phpunit/phpunit=5.7.*"
+      7.1|7.0|nightly)
+        echo "Using PHPUnit 6.1"
+        composer global require "phpunit/phpunit=6.1.*"
         ;;
       5.6|5.5|5.4|5.3)
         echo "Using PHPUnit 4.8"
@@ -94,11 +85,7 @@ before_script:
 - mysql --version
 - phpenv versions
 - php --version
-- |
-  # Debug PHP extensions, but not on HHVM because the command hangs indefinitely:
-  if [[ "$TRAVIS_PHP_VERSION" != 'hhvm' ]]; then
-    php -m
-  fi
+- php -m
 - npm --version
 - node --version
 - which phpunit
@@ -107,6 +94,7 @@ before_script:
 - grunt --version
 - git --version
 - svn --version
+- locale -a
 script: grunt $WP_TRAVISCI
 notifications:
   slack:
diff --git a/Gruntfile.js b/Gruntfile.js
index 80e2968..14e558f 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -426,23 +426,23 @@ module.exports = function(grunt) {
 		phpunit: {
 			'default': {
 				cmd: 'phpunit',
-				args: ['-c', 'phpunit.xml.dist']
+				args: ['--verbose', '-c', 'phpunit.xml.dist']
 			},
 			ajax: {
 				cmd: 'phpunit',
-				args: ['-c', 'phpunit.xml.dist', '--group', 'ajax']
+				args: ['--verbose', '-c', 'phpunit.xml.dist', '--group', 'ajax']
 			},
 			multisite: {
 				cmd: 'phpunit',
-				args: ['-c', 'tests/phpunit/multisite.xml']
+				args: ['--verbose', '-c', 'tests/phpunit/multisite.xml']
 			},
 			'external-http': {
 				cmd: 'phpunit',
-				args: ['-c', 'phpunit.xml.dist', '--group', 'external-http']
+				args: ['--verbose', '-c', 'phpunit.xml.dist', '--group', 'external-http']
 			},
 			'restapi-jsclient': {
 				cmd: 'phpunit',
-				args: ['-c', 'phpunit.xml.dist', '--group', 'restapi-jsclient']
+				args: ['--verbose', '-c', 'phpunit.xml.dist', '--group', 'restapi-jsclient']
 			}
 		},
 		uglify: {
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 415ec76..0bb6491 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -2,6 +2,7 @@
 	bootstrap="tests/phpunit/includes/bootstrap.php"
         backupGlobals="false"
         colors="true"
+        beStrictAboutTestsThatDoNotTestAnything="true"
         >
     <testsuites>
         <!-- Default test suite to run all tests -->
@@ -23,6 +24,7 @@
         <exclude>
             <group>ajax</group>
             <group>ms-files</group>
+            <group>ms-required</group>
             <group>external-http</group>
         </exclude>
     </groups>
diff --git a/src/wp-admin/css/color-picker.css b/src/wp-admin/css/color-picker.css
index baa1d94..30dec29 100644
--- a/src/wp-admin/css/color-picker.css
+++ b/src/wp-admin/css/color-picker.css
@@ -133,4 +133,11 @@
 	box-shadow:
 		0 0 0 1px #5b9dd9,
 		0 0 2px 1px rgba(30, 140, 190, .8);
-}
\ No newline at end of file
+}
+
+@media screen and ( max-width: 782px ) {
+	.wp-picker-container input[type="text"].wp-color-picker {
+		margin-right: 6px;
+		padding: 3px 5px;
+	}
+}
diff --git a/src/wp-admin/css/common.css b/src/wp-admin/css/common.css
index d06bebe..9074743 100644
--- a/src/wp-admin/css/common.css
+++ b/src/wp-admin/css/common.css
@@ -865,8 +865,6 @@ hr {
 	border-bottom: 1px solid #fafafa;
 }
 
-.widget-control-remove,
-.widget-control-remove:focus,
 .row-actions span.delete a,
 .row-actions span.trash a,
 .row-actions span.spam a,
@@ -893,14 +891,10 @@ span.required,
 #media-items a.delete:hover,
 #media-items a.delete-permanently:hover,
 #nav-menu-footer .menu-delete:hover {
-	color: #f00;
+	color: #dc3232;
 	border: none;
 }
 
-.widget-control-remove:hover {
-	color: #f00;
-}
-
 /*------------------------------------------------------------------------------
   3.0 - Actions
 ------------------------------------------------------------------------------*/
@@ -3086,8 +3080,8 @@ img {
 /* Metabox collapse arrow indicators */
 .js .sidebar-name .sidebar-name-arrow:before,
 .js .meta-box-sortables .postbox .toggle-indicator:before,
-.bulk-action-notice .toggle-indicator:before {
-	content: "\f142";
+.bulk-action-notice .toggle-indicator:before,
+.js .accordion-section .handlediv .sidebar-name-arrow:before {
 	display: inline-block;
 	font: normal 20px/1 dashicons;
 	speak: none;
@@ -3096,17 +3090,22 @@ img {
 	text-decoration: none !important;
 }
 
+/* Open state */
+.js .sidebar-name .sidebar-name-arrow:before,
+.js .meta-box-sortables .postbox .toggle-indicator:before,
+.bulk-action-notice .toggle-indicator:before,
+.js .accordion-section.open .handlediv .sidebar-name-arrow:before {
+	content: "\f142";
+}
+
+/* Closed state */
 .js .widgets-holder-wrap.closed .sidebar-name-arrow:before,
 .js .meta-box-sortables .postbox.closed .handlediv .toggle-indicator:before,
-.bulk-action-notice .bulk-action-errors-collapsed .toggle-indicator:before {
+.bulk-action-notice .bulk-action-errors-collapsed .toggle-indicator:before,
+.js .accordion-section .handlediv .sidebar-name-arrow:before {
 	content: "\f140";
 }
 
-.js .sidebar-name .sidebar-name-arrow:before {
-	padding: 10px;
-	left: 0;
-}
-
 .js #widgets-left .sidebar-name .sidebar-name-arrow {
 	display: none;
 }
@@ -3149,6 +3148,36 @@ img {
 		0 0 2px 1px rgba(30, 140, 190, .8);
 }
 
+.js .metabox-holder .accordion-section .accordion-section-title:after {
+	content: none;
+}
+
+.js .metabox-holder .accordion-section .handlediv {
+	float: right;
+	position: relative;
+	top: 11px;
+	margin-right: 11px;
+	z-index: 2;
+}
+
+.js .metabox-holder .accordion-section .handlediv .sidebar-name-arrow:before {
+	width: 20px;
+	border-radius: 50%;
+	text-indent: -1px; /* account for the dashicon alignment */
+}
+
+.js .metabox-holder .accordion-section .handlediv:focus {
+	outline: none;
+	border: none;
+	box-shadow: none;
+}
+
+.js .metabox-holder .accordion-section .handlediv:focus .sidebar-name-arrow:before {
+	box-shadow:
+		0 0 0 1px #5b9dd9,
+		0 0 2px 1px rgba(30, 140, 190, .8);
+}
+
 /* @todo: appears to be Press This only and overridden */
 #photo-add-url-div input[type="text"] {
 	width: 300px;
@@ -3244,25 +3273,26 @@ img {
 
 /* @todo: can we use a common class for these? */
 .nav-menus-php .item-edit:before,
-.widget-top a.widget-action:after,
+.widget-top .widget-action .toggle-indicator:before,
 .control-section .accordion-section-title:after,
-.accordion-section-title:after {
+.accordion-section-title:after,
+.accordion-section-arrow:after {
 	right: 0;
 	content: "\f140";
-	border: none;
-	background: none;
 	font: normal 20px/1 dashicons;
 	speak: none;
 	display: block;
-	padding: 0;
-	text-indent: 0;
-	text-align: center;
-	position: relative;
 	-webkit-font-smoothing: antialiased;
 	-moz-osx-font-smoothing: grayscale;
 	text-decoration: none !important;
 }
 
+.widget-top .widget-action .toggle-indicator:before {
+	padding: 1px 2px 1px 0px;
+	-webkit-border-radius: 50%;
+	border-radius: 50%;
+}
+
 .handlediv,
 .postbox .handlediv.button-link,
 .item-edit,
@@ -3288,15 +3318,7 @@ img {
 	color: #23282d;
 }
 
-.widget-top a.widget-action:after {
-	padding: 1px 2px 1px 0px;
-	margin-top: 10px;
-	margin-right: 10px;
-	-webkit-border-radius: 50%;
-	border-radius: 50%;
-}
-
-.widget-top a.widget-action:focus:after {
+.widget-top .widget-action:focus .toggle-indicator:before {
 	-webkit-box-shadow:
 		0 0 0 1px #5b9dd9,
 		0 0 2px 1px rgba(30,140,190,.8);
@@ -3315,7 +3337,8 @@ img {
 .control-section.open .accordion-section-title:after,
 #customize-info.open .accordion-section-title:after,
 .nav-menus-php .menu-item-edit-active .item-edit:before,
-.widget.open .widget-top a.widget-action:after {
+.widget.open .widget-top .widget-action .toggle-indicator:before,
+.open .accordion-section-arrow:after {
 	content: "\f142";
 }
 
@@ -3594,8 +3617,8 @@ img {
 	/* @todo: evaluate - most of these were likely replaced by dashicons */
 	.curtime #timestamp,
 	#screen-meta-links a.show-settings,
-	.widget-top a.widget-action,
-	.widget-top a.widget-action:hover,
+	.widget-top .widget-action,
+	.widget-top .widget-action:hover,
 	.sidebar-name-arrow,
 	.sidebar-name:hover .sidebar-name-arrow,
 	.meta-box-sortables .postbox:hover .handlediv,
diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css
index fbcfe64..d033b89 100644
--- a/src/wp-admin/css/customize-controls.css
+++ b/src/wp-admin/css/customize-controls.css
@@ -617,7 +617,6 @@ p.customize-section-description {
 
 .customize-control select {
 	width: 100%;
-	max-width: 300px;
 	height: 28px;
 	line-height: 28px;
 }
@@ -744,7 +743,6 @@ p.customize-section-description {
 
 .customize-control-dropdown-pages .new-content-item {
 	width: 100%;
-	max-width: 300px;
 	padding: 5px 0 5px 1px;
 	position: relative;
 }
@@ -1189,20 +1187,18 @@ p.customize-section-description {
 .customize-section-description-container + #customize-control-custom_css:last-child {
 	margin-left: -12px;
 	width: 299px;
+	width: -webkit-calc( 100% + 24px );
+	width: calc( 100% + 24px );
 	margin-bottom: -12px;
 }
 
 @media screen and ( max-width: 640px ) {
 	.customize-section-description-container + #customize-control-custom_css:last-child {
-		margin-left: 0;
 		margin-right: 0;
-		width: 100%;
 	}
 	.customize-section-description-container + #customize-control-custom_css:last-child textarea {
 		height: -webkit-calc( 100vh - 140px );
 		height: calc( 100vh - 140px );
-		width: 100%;
-		border: solid 1px #ddd;
 	}
 }
 
diff --git a/src/wp-admin/css/customize-nav-menus.css b/src/wp-admin/css/customize-nav-menus.css
index c42fa54..f2186fb 100644
--- a/src/wp-admin/css/customize-nav-menus.css
+++ b/src/wp-admin/css/customize-nav-menus.css
@@ -153,7 +153,7 @@
 	text-align: center;
 }
 
-.wp-customizer .menu-item.menu-item-edit-active .item-edit .toggle-indicator:after {
+.wp-customizer .menu-item.menu-item-edit-active .item-edit .toggle-indicator:before {
 	content: "\f142";
 }
 
@@ -271,17 +271,18 @@
 	display: inline-block;
 	font-size: 20px;
 	line-height: 1;
-	text-indent: -1px; /* account for the dashicon alignment */
 }
 
 .rtl .wp-customizer .toggle-indicator {
 	text-indent: 1px; /* account for the dashicon alignment */
 }
 
-.wp-customizer .toggle-indicator:after {
+.wp-customizer .menu-item .item-edit .toggle-indicator:before,
+#available-menu-items .accordion-section-title .toggle-indicator:before {
 	content: "\f140";
+	display: block;
+	padding: 1px 2px 1px 0px;
 	speak: none;
-	vertical-align: top;
 	-webkit-border-radius: 50%;
 	border-radius: 50%;
 	color: #72777c;
@@ -494,13 +495,13 @@
 	content: none !important;
 }
 
-#available-menu-items .accordion-section-title:hover .toggle-indicator:after,
-#available-menu-items .button-link:hover .toggle-indicator:after,
-#available-menu-items .button-link:focus .toggle-indicator:after {
+#available-menu-items .accordion-section-title:hover .toggle-indicator:before,
+#available-menu-items .button-link:hover .toggle-indicator:before,
+#available-menu-items .button-link:focus .toggle-indicator:before {
 	color: #23282d;
 }
 
-#available-menu-items .open .accordion-section-title .toggle-indicator:after {
+#available-menu-items .open .accordion-section-title .toggle-indicator:before {
 	content: "\f142";
 	color: #23282d;
 }
@@ -837,7 +838,7 @@ li.assigned-to-menu-location .add-new-menu-item {
 .wp-customizer .menu-item .submitbox .submitdelete:focus,
 .customize-screen-options-toggle:focus:before,
 #customize-controls .customize-info .customize-help-toggle:focus:before,
-.wp-customizer button:focus .toggle-indicator:after,
+.wp-customizer button:focus .toggle-indicator:before,
 .menu-delete:focus,
 .menu-item-bar .item-delete:focus:before,
 #available-menu-items .item-add:focus:before {
diff --git a/src/wp-admin/css/customize-widgets.css b/src/wp-admin/css/customize-widgets.css
index 687c9f8..428c455 100644
--- a/src/wp-admin/css/customize-widgets.css
+++ b/src/wp-admin/css/customize-widgets.css
@@ -89,15 +89,15 @@
 	line-height: 16px;
 }
 
-.customize-control-widget_form.expanded a.widget-action:after {
+.customize-control-widget_form.expanded .widget-action .toggle-indicator:before {
 	content: "\f142";
 }
 
-.customize-control-widget_form.wide-widget-control a.widget-action:after {
+.customize-control-widget_form.wide-widget-control .widget-action .toggle-indicator:before {
 	content: "\f139";
 }
 
-.customize-control-widget_form.wide-widget-control.expanded a.widget-action:after {
+.customize-control-widget_form.wide-widget-control.expanded .widget-action .toggle-indicator:before {
 	content: "\f141";
 }
 
@@ -438,9 +438,13 @@ body.adding-widget #customize-preview {
 	.last-widget {
 		margin-bottom: 15px;
 	}
+	/* This rule reduces the widgets titles height. */
 	.widget-title h3 {
 		padding: 13px 15px;
 	}
+	.widget-top .widget-action {
+		padding-bottom: 8px;
+	}
 	.widget-reorder-nav span {
 		height: 39px;
 	}
diff --git a/src/wp-admin/css/dashboard.css b/src/wp-admin/css/dashboard.css
index d467f0c..98fb993 100644
--- a/src/wp-admin/css/dashboard.css
+++ b/src/wp-admin/css/dashboard.css
@@ -308,19 +308,21 @@
 	padding: 0;
 }
 
-#dashboard_primary .widget-loading,
-#dashboard_primary .dashboard-widget-control-form {
+#dashboard_primary .widget-loading {
 	padding: 12px 12px 0;
+	margin-bottom: 1em !important; /* Needs to override `.postbox .inside > p:last-child` in common.css */
 }
 
-body #dashboard-widgets .postbox form .submit {
+/* Notice when JS is off. */
+#dashboard_primary .inside .notice {
 	margin: 0;
 }
 
-.dashboard-widget-control-form {
-	overflow: hidden;
+body #dashboard-widgets .postbox form .submit {
+	margin: 0;
 }
 
+/* Used only for configurable widgets. */
 .dashboard-widget-control-form p {
 	margin-top: 0;
 }
diff --git a/src/wp-admin/css/themes.css b/src/wp-admin/css/themes.css
index a18a89a..86fc133 100644
--- a/src/wp-admin/css/themes.css
+++ b/src/wp-admin/css/themes.css
@@ -1332,7 +1332,9 @@ body.full-overlay-active {
 	-moz-box-sizing: border-box;
 	box-sizing: border-box;
 	position: fixed;
-	width: 300px;
+	min-width: 300px;
+	max-width: 600px;
+	width: 18%;
 	height: 100%;
 	top: 0;
 	bottom: 0;
@@ -1361,6 +1363,26 @@ body.full-overlay-active {
 	margin-left: -300px;
 }
 
+@media screen and (min-width: 1667px) {
+	.wp-full-overlay.expanded {
+		margin-left: 18%;
+	}
+
+	.wp-full-overlay.collapsed .wp-full-overlay-sidebar {
+		margin-left: -18%;
+	}
+}
+
+@media screen and (min-width: 3333px) {
+	.wp-full-overlay.expanded {
+		margin-left: 600px;
+	}
+
+	.wp-full-overlay.collapsed .wp-full-overlay-sidebar {
+		margin-left: -600px;
+	}
+}
+
 .wp-full-overlay-sidebar:after {
 	content: "";
 	display: block;
@@ -1520,7 +1542,6 @@ body.full-overlay-active {
 	box-shadow: none !important;
 	-webkit-border-radius: 0 !important;
 	border-radius: 0 !important;
-	z-index: -1; /* Below device buttons */
 }
 
 .wp-core-ui .wp-full-overlay .collapse-sidebar:hover,
@@ -1616,14 +1637,20 @@ body.full-overlay-active {
 	position: fixed;
 	bottom: 0;
 	left: 0;
-	width: 299px;
+	min-width: 299px;
+	max-width: 599px;
+	width: 18%;
 	height: 45px;
 	border-top: 1px solid #ddd;
 	background: #eee;
 }
 
-.wp-full-overlay-footer .devices {
+.wp-full-overlay-footer .devices-wrapper {
 	float: right;
+}
+
+.wp-full-overlay-footer .devices {
+	position: relative;
 	background: #eee;
 	-webkit-box-shadow: -20px 0 10px -5px #eee;
 	box-shadow: -20px 0 10px -5px #eee;
diff --git a/src/wp-admin/css/widgets.css b/src/wp-admin/css/widgets.css
index f0be076..bc794e5 100644
--- a/src/wp-admin/css/widgets.css
+++ b/src/wp-admin/css/widgets.css
@@ -14,12 +14,13 @@
 	background: #f7f7f7;
 }
 
-.widget-top a.widget-action,
-.widget-top a.widget-action:hover {
-	-webkit-box-shadow: none;
-	box-shadow: none;
+.widget-top .widget-action {
+	border: 0;
+	margin: 0;
+	padding: 10px;
+	background: none;
+	cursor: pointer;
 	outline: none;
-	text-decoration: none;
 }
 
 .widget-title h3,
@@ -50,7 +51,7 @@
 }
 
 .deleting .widget-title,
-.deleting .widget-top a.widget-action:after {
+.deleting .widget-top .widget-action .toggle-indicator:before {
 	color: #a0a5aa;
 }
 
@@ -91,11 +92,37 @@
 	box-sizing: border-box;
 }
 
-.sidebar-name-arrow {
-	position: absolute;
-	top: 0;
-	right: 0;
-	bottom: 0;
+.js .sidebar-name .handlediv {
+	float: right;
+    width: 32px;
+    height: 32px;
+    padding: 0;
+}
+
+.js .sidebar-name .handlediv:focus {
+	box-shadow: none;
+	outline: none;
+}
+
+.js #widgets-left .sidebar-name .handlediv:focus .sidebar-name-arrow {
+	display: block;
+}
+
+.js .sidebar-name .handlediv:focus .sidebar-name-arrow:before {
+	box-shadow:
+		0 0 0 1px #5b9dd9,
+		0 0 2px 1px rgba(30, 140, 190, .8);
+}
+
+.js .sidebar-name .sidebar-name-arrow:before {
+	margin-top: 14px;
+	width: 20px;
+	border-radius: 50%;
+	text-indent: -1px; /* account for the dashicon alignment */
+}
+
+.js .widget-liquid-left .sidebar-name .sidebar-name-arrow:before {
+	margin-top: 7px;
 }
 
 .js .sidebar-name {
@@ -156,10 +183,6 @@ div#widgets-left .sidebar-name h3 {
 	margin: 0 10px 0 0;
 }
 
-#widgets-left .sidebar-name .sidebar-name-arrow:before {
-	padding: 9px;
-}
-
 #widgets-left .widgets-holder-wrap,
 div#widgets-left .widget-holder {
 	background: transparent;
@@ -249,10 +272,6 @@ div#widgets-right .sidebar-name h3 {
 	padding: 15px 7px;
 }
 
-div#widgets-right .sidebar-name .sidebar-name-arrow:before {
-	top: 2px;
-}
-
 div#widgets-right .widget-top {
 	padding: 0;
 }
diff --git a/src/wp-admin/customize.php b/src/wp-admin/customize.php
index f1e66ff..b6c1e63 100644
--- a/src/wp-admin/customize.php
+++ b/src/wp-admin/customize.php
@@ -171,30 +171,32 @@
 		</div>
 
 		<div id="customize-footer-actions" class="wp-full-overlay-footer">
-			<?php $previewable_devices = $wp_customize->get_previewable_devices(); ?>
-			<?php if ( ! empty( $previewable_devices ) ) : ?>
-			<div class="devices">
-				<?php foreach ( (array) $previewable_devices as $device => $settings ) : ?>
-					<?php
-					if ( empty( $settings['label'] ) ) {
-						continue;
-					}
-					$active = ! empty( $settings['default'] );
-					$class = 'preview-' . $device;
-					if ( $active ) {
-						$class .= ' active';
-					}
-					?>
-					<button type="button" class="<?php echo esc_attr( $class ); ?>" aria-pressed="<?php echo esc_attr( $active ) ?>" data-device="<?php echo esc_attr( $device ); ?>">
-						<span class="screen-reader-text"><?php echo esc_html( $settings['label'] ); ?></span>
-					</button>
-				<?php endforeach; ?>
-			</div>
-			<?php endif; ?>
 			<button type="button" class="collapse-sidebar button" aria-expanded="true" aria-label="<?php echo esc_attr( _x( 'Hide Controls', 'label for hide controls button without length constraints' ) ); ?>">
 				<span class="collapse-sidebar-arrow"></span>
 				<span class="collapse-sidebar-label"><?php _ex( 'Hide Controls', 'short (~12 characters) label for hide controls button' ); ?></span>
 			</button>
+			<?php $previewable_devices = $wp_customize->get_previewable_devices(); ?>
+			<?php if ( ! empty( $previewable_devices ) ) : ?>
+			<div class="devices-wrapper">
+				<div class="devices">
+					<?php foreach ( (array) $previewable_devices as $device => $settings ) : ?>
+						<?php
+						if ( empty( $settings['label'] ) ) {
+							continue;
+						}
+						$active = ! empty( $settings['default'] );
+						$class = 'preview-' . $device;
+						if ( $active ) {
+							$class .= ' active';
+						}
+						?>
+						<button type="button" class="<?php echo esc_attr( $class ); ?>" aria-pressed="<?php echo esc_attr( $active ) ?>" data-device="<?php echo esc_attr( $device ); ?>">
+							<span class="screen-reader-text"><?php echo esc_html( $settings['label'] ); ?></span>
+						</button>
+					<?php endforeach; ?>
+				</div>
+			</div>
+			<?php endif; ?>
 		</div>
 	</form>
 	<div id="customize-preview" class="wp-full-overlay-main"></div>
diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php
index 90a9dd1..3342cad 100644
--- a/src/wp-admin/includes/ajax-actions.php
+++ b/src/wp-admin/includes/ajax-actions.php
@@ -1497,7 +1497,10 @@ function wp_ajax_wp_link_ajax() {
 
 	$args['pagenum'] = ! empty( $_POST['page'] ) ? absint( $_POST['page'] ) : 1;
 
-	require(ABSPATH . WPINC . '/class-wp-editor.php');
+	if ( ! class_exists( '_WP_Editors', false ) ) {
+		require( ABSPATH . WPINC . '/class-wp-editor.php' );
+	}
+
 	$results = _WP_Editors::wp_link_query( $args );
 
 	if ( ! isset( $results ) )
diff --git a/src/wp-admin/includes/dashboard.php b/src/wp-admin/includes/dashboard.php
index b763d43..be0b201 100644
--- a/src/wp-admin/includes/dashboard.php
+++ b/src/wp-admin/includes/dashboard.php
@@ -191,7 +191,7 @@ function wp_add_dashboard_widget( $widget_id, $widget_name, $callback, $control_
  * @param array $meta_box
  */
 function _wp_dashboard_control_callback( $dashboard, $meta_box ) {
-	echo '<form method="post" class="dashboard-widget-control-form">';
+	echo '<form method="post" class="dashboard-widget-control-form wp-clearfix">';
 	wp_dashboard_trigger_widget_control( $meta_box['id'] );
 	wp_nonce_field( 'edit-dashboard-widget_' . $meta_box['id'], 'dashboard-widget-nonce' );
 	echo '<input type="hidden" name="widget_id" value="' . esc_attr($meta_box['id']) . '" />';
@@ -968,7 +968,7 @@ function wp_dashboard_rss_output( $widget_id ) {
  * @return bool False on failure. True on success.
  */
 function wp_dashboard_cached_rss_widget( $widget_id, $callback, $check_urls = array() ) {
-	$loading = '<p class="widget-loading hide-if-no-js">' . __( 'Loading&#8230;' ) . '</p><p class="hide-if-js">' . __( 'This widget requires JavaScript.' ) . '</p>';
+	$loading = '<p class="widget-loading hide-if-no-js">' . __( 'Loading&#8230;' ) . '</p><div class="hide-if-js notice notice-error inline"><p>' . __( 'This widget requires JavaScript.' ) . '</p></div>';
 	$doing_ajax = wp_doing_ajax();
 
 	if ( empty($check_urls) ) {
diff --git a/src/wp-admin/includes/meta-boxes.php b/src/wp-admin/includes/meta-boxes.php
index 23ca247..ba43c7d 100644
--- a/src/wp-admin/includes/meta-boxes.php
+++ b/src/wp-admin/includes/meta-boxes.php
@@ -50,10 +50,16 @@ function post_submit_meta_box( $post, $args = array() ) {
 <?php
 $preview_link = esc_url( get_preview_post_link( $post ) );
 if ( 'publish' == $post->post_status ) {
-	$preview_button = __( 'Preview Changes' );
+	$preview_button_text = __( 'Preview Changes' );
 } else {
-	$preview_button = __( 'Preview' );
+	$preview_button_text = __( 'Preview' );
 }
+
+$preview_button = sprintf( '%1$s<span class="screen-reader-text"> %2$s</span>',
+	$preview_button_text,
+	/* translators: accessibility text */
+	__( '(link opens in a new window)' )
+);
 ?>
 <a class="preview button" href="<?php echo $preview_link; ?>" target="wp-preview-<?php echo (int) $post->ID; ?>" id="post-preview"><?php echo $preview_button; ?></a>
 <input type="hidden" name="wp-preview" id="wp-preview" value="" />
diff --git a/src/wp-admin/includes/ms.php b/src/wp-admin/includes/ms.php
index 6a4ca9d..939d0d0 100644
--- a/src/wp-admin/includes/ms.php
+++ b/src/wp-admin/includes/ms.php
@@ -785,7 +785,7 @@ function mu_dropdown_languages( $lang_files = array(), $current = '' ) {
 function site_admin_notice() {
 	global $wp_db_version, $pagenow;
 
-	if ( ! is_super_admin() ) {
+	if ( ! current_user_can( 'upgrade_network' ) ) {
 		return false;
 	}
 
diff --git a/src/wp-admin/includes/options.php b/src/wp-admin/includes/options.php
index 95d9964..14c175d 100644
--- a/src/wp-admin/includes/options.php
+++ b/src/wp-admin/includes/options.php
@@ -53,7 +53,7 @@ function options_general_add_js() {
 			if ( "date_format_custom_radio" != $(this).attr("id") )
 				$( "input[name='date_format_custom']" ).val( $( this ).val() ).siblings( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() );
 		});
-		$("input[name='date_format_custom']").focus(function(){
+		$( 'input[name="date_format_custom"]' ).on( 'click input', function() {
 			$( '#date_format_custom_radio' ).prop( 'checked', true );
 		});
 
@@ -61,7 +61,7 @@ function options_general_add_js() {
 			if ( "time_format_custom_radio" != $(this).attr("id") )
 				$( "input[name='time_format_custom']" ).val( $( this ).val() ).siblings( '.example' ).text( $( this ).parent( 'label' ).children( '.format-i18n' ).text() );
 		});
-		$("input[name='time_format_custom']").focus(function(){
+		$( 'input[name="time_format_custom"]' ).on( 'click input', function() {
 			$( '#time_format_custom_radio' ).prop( 'checked', true );
 		});
 		$("input[name='date_format_custom'], input[name='time_format_custom']").change( function() {
diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php
index 1054f2b..8055a7b 100644
--- a/src/wp-admin/includes/post.php
+++ b/src/wp-admin/includes/post.php
@@ -1448,25 +1448,34 @@ function _wp_post_thumbnail_html( $thumbnail_id = null, $post = null ) {
  *
  * @since 2.5.0
  *
- * @param int $post_id ID of the post to check for editing
- * @return integer False: not locked or locked by current user. Int: user ID of user with lock.
+ * @param int $post_id ID of the post to check for editing.
+ * @return int|false ID of the user with lock. False if the post does not exist, post is not locked,
+ *                   the user with lock does not exist, or the post is locked by current user.
  */
 function wp_check_post_lock( $post_id ) {
-	if ( !$post = get_post( $post_id ) )
+	if ( ! $post = get_post( $post_id ) ) {
 		return false;
+	}
 
-	if ( !$lock = get_post_meta( $post->ID, '_edit_lock', true ) )
+	if ( ! $lock = get_post_meta( $post->ID, '_edit_lock', true ) ) {
 		return false;
+	}
 
 	$lock = explode( ':', $lock );
 	$time = $lock[0];
 	$user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true );
 
+	if ( ! get_userdata( $user ) ) {
+		return false;
+	}
+
 	/** This filter is documented in wp-admin/includes/ajax-actions.php */
 	$time_window = apply_filters( 'wp_check_post_lock_window', 150 );
 
-	if ( $time && $time > time() - $time_window && $user != get_current_user_id() )
+	if ( $time && $time > time() - $time_window && $user != get_current_user_id() ) {
 		return $user;
+	}
+
 	return false;
 }
 
@@ -1475,20 +1484,24 @@ function wp_check_post_lock( $post_id ) {
  *
  * @since 2.5.0
  *
- * @param int $post_id ID of the post to being edited
- * @return bool|array Returns false if the post doesn't exist of there is no current user, or
- * 	an array of the lock time and the user ID.
+ * @param int $post_id ID of the post being edited.
+ * @return array|false Array of the lock time and user ID. False if the post does not exist, or
+ *                     there is no current user.
  */
 function wp_set_post_lock( $post_id ) {
-	if ( !$post = get_post( $post_id ) )
+	if ( ! $post = get_post( $post_id ) ) {
 		return false;
-	if ( 0 == ($user_id = get_current_user_id()) )
+	}
+
+	if ( 0 == ( $user_id = get_current_user_id() ) ) {
 		return false;
+	}
 
 	$now = time();
 	$lock = "$now:$user_id";
 
 	update_post_meta( $post->ID, '_edit_lock', $lock );
+
 	return array( $now, $user_id );
 }
 
diff --git a/src/wp-admin/includes/schema.php b/src/wp-admin/includes/schema.php
index 70b4686..64ec6b7 100644
--- a/src/wp-admin/includes/schema.php
+++ b/src/wp-admin/includes/schema.php
@@ -951,12 +951,16 @@ function populate_network( $network_id = 1, $domain = '', $email = '', $site_nam
 
 	if ( !is_multisite() ) {
 		$site_admins = array( $site_user->user_login );
-		$users = get_users( array( 'fields' => array( 'ID', 'user_login' ) ) );
+		$users = get_users( array(
+			'fields' => array( 'user_login' ),
+			'role'   => 'administrator',
+		) );
 		if ( $users ) {
 			foreach ( $users as $user ) {
-				if ( is_super_admin( $user->ID ) && !in_array( $user->user_login, $site_admins ) )
-					$site_admins[] = $user->user_login;
+				$site_admins[] = $user->user_login;
 			}
+
+			$site_admins = array_unique( $site_admins );
 		}
 	} else {
 		$site_admins = get_site_option( 'site_admins' );
diff --git a/src/wp-admin/includes/template.php b/src/wp-admin/includes/template.php
index a4672fd..f568e39 100644
--- a/src/wp-admin/includes/template.php
+++ b/src/wp-admin/includes/template.php
@@ -1161,9 +1161,12 @@ function do_accordion_sections( $screen, $context, $object ) {
 					}
 					?>
 					<li class="control-section accordion-section <?php echo $hidden_class; ?> <?php echo $open_class; ?> <?php echo esc_attr( $box['id'] ); ?>" id="<?php echo esc_attr( $box['id'] ); ?>">
-						<h3 class="accordion-section-title hndle" tabindex="0">
+						<button type="button" class="handlediv button-link" aria-expanded="true">
+							<span class="screen-reader-text"><?php echo sprintf( __( 'Toggle panel: %s' ), esc_html( $box['title'] ) ); ?></span>
+							<span class="sidebar-name-arrow" aria-hidden="true"></span>
+						</button>
+						<h3 class="accordion-section-title hndle">
 							<?php echo esc_html( $box['title'] ); ?>
-							<span class="screen-reader-text"><?php _e( 'Press return or enter to open this section' ); ?></span>
 						</h3>
 						<div class="accordion-section-content <?php postbox_classes( $box['id'], $page ); ?>">
 							<div class="inside">
diff --git a/src/wp-admin/includes/widgets.php b/src/wp-admin/includes/widgets.php
index fe2a957..408165c 100644
--- a/src/wp-admin/includes/widgets.php
+++ b/src/wp-admin/includes/widgets.php
@@ -80,7 +80,10 @@ function wp_list_widget_controls( $sidebar, $sidebar_name = '' ) {
 	if ( $sidebar_name ) {
 		?>
 		<div class="sidebar-name">
-			<div class="sidebar-name-arrow"><br /></div>
+			<button type="button" class="handlediv button-link" aria-expanded="true">
+				<span class="screen-reader-text"><?php echo sprintf( __( 'Toggle panel: %s' ), $sidebar_name ); ?></span>
+				<span class="sidebar-name-arrow" aria-hidden="true"></span>
+			</button>
 			<h2><?php echo esc_html( $sidebar_name ); ?> <span class="spinner"></span></h2>
 		</div>
 		<?php
@@ -219,7 +222,10 @@ function wp_widget_control( $sidebar_args ) {
 	echo $sidebar_args['before_widget']; ?>
 	<div class="widget-top">
 	<div class="widget-title-action">
-		<a class="widget-action hide-if-no-js" href="#available-widgets"></a>
+		<button type="button" class="widget-action hide-if-no-js" aria-expanded="false">
+			<span class="screen-reader-text"><?php printf( __( 'Edit widget: %s' ), $widget_title ); ?></span>
+			<span class="toggle-indicator" aria-hidden="true"></span>
+		</button>
 		<a class="widget-control-edit hide-if-js" href="<?php echo esc_url( add_query_arg( $query_arg ) ); ?>">
 			<span class="edit"><?php _ex( 'Edit', 'widget' ); ?></span>
 			<span class="add"><?php _ex( 'Add', 'widget' ); ?></span>
@@ -250,8 +256,8 @@ function wp_widget_control( $sidebar_args ) {
 
 	<div class="widget-control-actions">
 		<div class="alignleft">
-		<a class="widget-control-remove" href="#remove"><?php _e('Delete'); ?></a> |
-		<a class="widget-control-close" href="#close"><?php _e('Close'); ?></a>
+			<button type="button" class="button-link button-link-delete widget-control-remove"><?php _e( 'Delete' ); ?></button> |
+			<button type="button" class="button-link widget-control-close"><?php _e( 'Close' ); ?></button>
 		</div>
 		<div class="alignright<?php if ( 'noform' === $has_form ) echo ' widget-control-noform'; ?>">
 			<?php submit_button( __( 'Save' ), 'primary widget-control-save right', 'savewidget', false, array( 'id' => 'widget-' . esc_attr( $id_format ) . '-savewidget' ) ); ?>
diff --git a/src/wp-admin/js/accordion.js b/src/wp-admin/js/accordion.js
index af62815..6eb919a 100644
--- a/src/wp-admin/js/accordion.js
+++ b/src/wp-admin/js/accordion.js
@@ -33,7 +33,7 @@
 	$( document ).ready( function () {
 
 		// Expand/Collapse accordion sections on click.
-		$( '.accordion-container' ).on( 'click keydown', '.accordion-section-title', function( e ) {
+		$( '.accordion-container' ).on( 'click keydown', '.accordion-section-title, .handlediv', function( e ) {
 			if ( e.type === 'keydown' && 13 !== e.which ) { // "return" key
 				return;
 			}
diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js
index e038dcc..d340a1b 100644
--- a/src/wp-admin/js/customize-controls.js
+++ b/src/wp-admin/js/customize-controls.js
@@ -4762,6 +4762,12 @@
 			 */
 			populateChangesetUuidParam = function( isIncluded ) {
 				var urlParser, queryParams;
+
+				// Abort on IE9 which doesn't support history management.
+				if ( ! history.replaceState ) {
+					return;
+				}
+
 				urlParser = document.createElement( 'a' );
 				urlParser.href = location.href;
 				queryParams = api.utils.parseQueryString( urlParser.search.substr( 1 ) );
@@ -4780,11 +4786,9 @@
 				history.replaceState( {}, document.title, urlParser.href );
 			};
 
-			if ( history.replaceState ) {
-				changesetStatus.bind( function( newStatus ) {
-					populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus );
-				} );
-			}
+			changesetStatus.bind( function( newStatus ) {
+				populateChangesetUuidParam( '' !== newStatus && 'publish' !== newStatus );
+			} );
 
 			// Expose states to the API.
 			api.state = state;
diff --git a/src/wp-admin/js/customize-widgets.js b/src/wp-admin/js/customize-widgets.js
index b482e8d..df4c8d7 100644
--- a/src/wp-admin/js/customize-widgets.js
+++ b/src/wp-admin/js/customize-widgets.js
@@ -952,7 +952,7 @@
 			var self = this, $removeBtn, replaceDeleteWithRemove;
 
 			// Configure remove button
-			$removeBtn = this.container.find( 'a.widget-control-remove' );
+			$removeBtn = this.container.find( '.widget-control-remove' );
 			$removeBtn.on( 'click', function( e ) {
 				e.preventDefault();
 
@@ -988,7 +988,7 @@
 			} );
 
 			replaceDeleteWithRemove = function() {
-				$removeBtn.text( l10n.removeBtnLabel ); // wp_widget_control() outputs the link as "Delete"
+				$removeBtn.text( l10n.removeBtnLabel ); // wp_widget_control() outputs the button as "Delete"
 				$removeBtn.attr( 'title', l10n.removeBtnTooltip );
 			};
 
@@ -1367,7 +1367,7 @@
 		 * @param {Object} args  merged on top of this.defaultActiveArguments
 		 */
 		onChangeExpanded: function ( expanded, args ) {
-			var self = this, $widget, $inside, complete, prevComplete, expandControl;
+			var self = this, $widget, $inside, complete, prevComplete, expandControl, $toggleBtn;
 
 			self.embedWidgetControl(); // Make sure the outer form is embedded so that the expanded state can be set in the UI.
 			if ( expanded ) {
@@ -1386,6 +1386,7 @@
 
 			$widget = this.container.find( 'div.widget:first' );
 			$inside = $widget.find( '.widget-inside:first' );
+			$toggleBtn = this.container.find( '.widget-top button.widget-action' );
 
 			expandControl = function() {
 
@@ -1399,6 +1400,7 @@
 				complete = function() {
 					self.container.removeClass( 'expanding' );
 					self.container.addClass( 'expanded' );
+					$toggleBtn.attr( 'aria-expanded', 'true' );
 					self.container.trigger( 'expanded' );
 				};
 				if ( args.completeCallback ) {
@@ -1428,10 +1430,10 @@
 					expandControl();
 				}
 			} else {
-
 				complete = function() {
 					self.container.removeClass( 'collapsing' );
 					self.container.removeClass( 'expanded' );
+					$toggleBtn.attr( 'aria-expanded', 'false' );
 					self.container.trigger( 'collapsed' );
 				};
 				if ( args.completeCallback ) {
diff --git a/src/wp-admin/js/editor.js b/src/wp-admin/js/editor.js
index a63a51c..917721a 100644
--- a/src/wp-admin/js/editor.js
+++ b/src/wp-admin/js/editor.js
@@ -1,5 +1,8 @@
+window.wp = window.wp || {};
+
+( function( $, wp ) {
+	wp.editor = wp.editor || {};
 
-( function( $ ) {
 	/**
 	 * @summary Utility functions for the editor.
 	 *
@@ -484,10 +487,8 @@
 			} );
 		}
 
-		window.wp = window.wp || {};
-		window.wp.editor = window.wp.editor || {};
-		window.wp.editor.autop = wpautop;
-		window.wp.editor.removep = pre_wpautop;
+		wp.editor.autop = wpautop;
+		wp.editor.removep = pre_wpautop;
 
 		exports = {
 			go: switchEditor,
@@ -505,4 +506,143 @@
 	 * Expose the switch editors to be used globally.
 	 */
 	window.switchEditors = new SwitchEditors();
-}( window.jQuery ));
+
+	/**
+	 * Initialize TinyMCE and/or Quicktags. For use with wp_enqueue_editor() (PHP).
+	 *
+	 * Intended for use with an existing textarea that will become the Text editor tab.
+	 * The editor width will be the width of the textarea container, height will be adjustable.
+	 *
+	 * Settings for both TinyMCE and Quicktags can be passed on initialization, and are "filtered"
+	 * with custom jQuery events on the document element, wp-before-tinymce-init and wp-before-quicktags-init.
+	 *
+	 * @since 4.8
+	 *
+	 * @param {string} id The HTML id of the textarea that is used for the editor.
+	 *                    Has to be jQuery compliant. No brackets, special chars, etc.
+	 * @param {object} settings Example:
+	 * settings = {
+	 *    // See https://www.tinymce.com/docs/configure/integration-and-setup/.
+	 *    // Alternatively set to `true` to use the defaults.
+	 *    tinymce: {
+	 *        setup: function( editor ) {
+	 *            console.log( 'Editor initialized', editor );
+	 *        }
+	 *    }
+	 *
+	 *    // Alternatively set to `true` to use the defaults.
+	 *	  quicktags: {
+	 *        buttons: 'strong,em,link'
+	 *    }
+	 * }
+	 */
+	wp.editor.initialize = function( id, settings ) {
+		var init;
+		var defaults;
+
+		if ( ! $ || ! id || ! wp.editor.getDefaultSettings ) {
+			return;
+		}
+
+		defaults = wp.editor.getDefaultSettings();
+
+		// Initialize TinyMCE by default
+		if ( ! settings ) {
+			settings = {
+				tinymce: true
+			};
+		}
+
+		// Add wrap and the Visual|Text tabs.
+		if ( settings.tinymce && settings.quicktags ) {
+			var $textarea = $( '#' + id );
+			var $wrap = $( '<div>' ).attr( {
+					'class': 'wp-core-ui wp-editor-wrap tmce-active',
+					id: 'wp-' + id + '-wrap'
+				} );
+			var $editorContainer = $( '<div class="wp-editor-container">' );
+			var $button = $( '<button>' ).attr( {
+					type: 'button',
+					'data-wp-editor-id': id
+				} );
+
+			$wrap.append(
+				$( '<div class="wp-editor-tools">' )
+					.append( $( '<div class="wp-editor-tabs">' )
+						.append( $button.clone().attr({
+							id: id + '-tmce',
+							'class': 'wp-switch-editor switch-tmce'
+						}).text( window.tinymce.translate( 'Visual' ) ) )
+						.append( $button.attr({
+							id: id + '-html',
+							'class': 'wp-switch-editor switch-html'
+						}).text( window.tinymce.translate( 'Text' ) ) )
+					).append( $editorContainer )
+			);
+
+			$textarea.after( $wrap );
+			$editorContainer.append( $textarea );
+		}
+
+		if ( window.tinymce && settings.tinymce ) {
+			if ( typeof settings.tinymce !== 'object' ) {
+				settings.tinymce = {};
+			}
+
+			init = $.extend( {}, defaults.tinymce, settings.tinymce );
+			init.selector = '#' + id;
+
+			$( document ).trigger( 'wp-before-tinymce-init', init );
+			window.tinymce.init( init );
+
+			if ( ! window.wpActiveEditor ) {
+				window.wpActiveEditor = id;
+			}
+		}
+
+		if ( window.quicktags && settings.quicktags ) {
+			if ( typeof settings.quicktags !== 'object' ) {
+				settings.quicktags = {};
+			}
+
+			init = $.extend( {}, defaults.quicktags, settings.quicktags );
+			init.id = id;
+
+			$( document ).trigger( 'wp-before-quicktags-init', init );
+			window.quicktags( init );
+
+			if ( ! window.wpActiveEditor ) {
+				window.wpActiveEditor = init.id;
+			}
+		}
+	};
+
+	/**
+	 * Get the editor content.
+	 *
+	 * Intended for use with editors that were initialized with wp.editor.initialize().
+	 *
+	 * @since 4.8
+	 *
+	 * @param {string} id The HTML id of the editor textarea.
+	 * @return The editor content.
+	 */
+	wp.editor.getContent = function( id ) {
+		var editor;
+
+		if ( ! $ || ! id ) {
+			return;
+		}
+
+		if ( window.tinymce ) {
+			editor = window.tinymce.get( id );
+
+			if ( editor && ! editor.isHidden() ) {
+				editor.save();
+			}
+		}
+
+		return $( '#' + id ).val();
+	};
+
+}( window.jQuery, window.wp ));
diff --git a/src/wp-admin/js/widgets.js b/src/wp-admin/js/widgets.js
index aa439ea..77cf8f9 100644
--- a/src/wp-admin/js/widgets.js
+++ b/src/wp-admin/js/widgets.js
@@ -41,7 +41,8 @@ wpWidgets = {
 		$(document.body).bind('click.widgets-toggle', function(e) {
 			var target = $(e.target),
 				css = { 'z-index': 100 },
-				widget, inside, targetWidth, widgetWidth, margin;
+				widget, inside, targetWidth, widgetWidth, margin,
+				toggleBtn = target.closest( '.widget' ).find( '.widget-top button.widget-action' );
 
 			if ( target.parents('.widget-top').length && ! target.parents('#available-widgets').length ) {
 				widget = target.closest('div.widget');
@@ -60,10 +61,21 @@ wpWidgets = {
 						css[ margin ] = widgetWidth - ( targetWidth + 30 ) + 'px';
 						widget.css( css );
 					}
-					widget.addClass( 'open' );
-					inside.slideDown('fast');
+					/*
+					 * Don't change the order of attributes changes and animation:
+					 * it's important for screen readers, see ticket #31476.
+					 */
+					toggleBtn.attr( 'aria-expanded', 'true' );
+					inside.slideDown( 'fast', function() {
+						widget.addClass( 'open' );
+					});
 				} else {
-					inside.slideUp('fast', function() {
+					/*
+					 * Don't change the order of attributes changes and animation:
+					 * it's important for screen readers, see ticket #31476.
+					 */
+					toggleBtn.attr( 'aria-expanded', 'false' );
+					inside.slideUp( 'fast', function() {
 						widget.attr( 'style', '' );
 						widget.removeClass( 'open' );
 					});
@@ -78,6 +90,7 @@ wpWidgets = {
 			} else if ( target.hasClass('widget-control-close') ) {
 				widget = target.closest('div.widget');
 				widget.removeClass( 'open' );
+				toggleBtn.attr( 'aria-expanded', 'false' );
 				wpWidgets.close( widget );
 				e.preventDefault();
 			} else if ( target.attr( 'id' ) === 'inactive-widgets-control-remove' ) {
@@ -92,7 +105,7 @@ wpWidgets = {
 			wpWidgets.appendTitle( this );
 
 			if ( $this.find( 'p.widget-error' ).length ) {
-				$this.find( 'a.widget-action' ).trigger('click');
+				$this.find( '.widget-action' ).trigger( 'click' ).attr( 'aria-expanded', 'true' );
 			}
 		});
 
@@ -182,6 +195,7 @@ wpWidgets = {
 
 				if ( inside.css('display') === 'block' ) {
 					ui.item.removeClass('open');
+					ui.item.find( '.widget-top button.widget-action' ).attr( 'aria-expanded', 'false' );
 					inside.hide();
 					$(this).sortable('refreshPositions');
 				}
@@ -254,7 +268,7 @@ wpWidgets = {
 				}
 
 				if ( addNew ) {
-					$widget.find( 'a.widget-action' ).trigger('click');
+					$widget.find( '.widget-action' ).trigger( 'click' );
 				} else {
 					wpWidgets.saveOrder( $sidebar.attr('id') );
 				}
@@ -492,7 +506,10 @@ wpWidgets = {
 
 	close : function(widget) {
 		widget.children('.widget-inside').slideUp('fast', function() {
-			widget.attr( 'style', '' );
+			widget.attr( 'style', '' )
+				.find( '.widget-top button.widget-action' )
+					.attr( 'aria-expanded', 'false' )
+					.focus();
 		});
 	},
 
diff --git a/src/wp-admin/network/menu.php b/src/wp-admin/network/menu.php
index 41281e7..e4cc786 100644
--- a/src/wp-admin/network/menu.php
+++ b/src/wp-admin/network/menu.php
@@ -19,7 +19,7 @@
 	$submenu['index.php'][10] = array( __( 'Updates' ), 'update_core', 'update-core.php' );
 }
 
-$submenu['index.php'][15] = array( __( 'Upgrade Network' ), 'manage_network', 'upgrade.php' );
+$submenu['index.php'][15] = array( __( 'Upgrade Network' ), 'upgrade_network', 'upgrade.php' );
 
 $menu[4] = array( '', 'read', 'separator1', '', 'wp-menu-separator' );
 
diff --git a/src/wp-admin/network/site-new.php b/src/wp-admin/network/site-new.php
index 0be051e..f36dffb 100644
--- a/src/wp-admin/network/site-new.php
+++ b/src/wp-admin/network/site-new.php
@@ -13,8 +13,9 @@
 /** WordPress Translation Install API */
 require_once( ABSPATH . 'wp-admin/includes/translation-install.php' );
 
-if ( ! current_user_can( 'manage_sites' ) )
+if ( ! current_user_can( 'create_sites' ) ) {
 	wp_die( __( 'Sorry, you are not allowed to add sites to this network.' ) );
+}
 
 get_current_screen()->add_help_tab( array(
 	'id'      => 'overview',
diff --git a/src/wp-admin/network/upgrade.php b/src/wp-admin/network/upgrade.php
index 5c2d3f9..46c6a94 100644
--- a/src/wp-admin/network/upgrade.php
+++ b/src/wp-admin/network/upgrade.php
@@ -32,8 +32,9 @@
 
 require_once( ABSPATH . 'wp-admin/admin-header.php' );
 
-if ( ! current_user_can( 'manage_network' ) )
+if ( ! current_user_can( 'upgrade_network' ) ) {
 	wp_die( __( 'Sorry, you are not allowed to access this page.' ), 403 );
+}
 
 echo '<div class="wrap">';
 echo '<h1>' . __( 'Upgrade Network' ) . '</h1>';
diff --git a/src/wp-admin/options-reading.php b/src/wp-admin/options-reading.php
index 69c425f..baa9f75 100644
--- a/src/wp-admin/options-reading.php
+++ b/src/wp-admin/options-reading.php
@@ -22,7 +22,7 @@
 	'title'   => __('Overview'),
 	'content' => '<p>' . __('This screen contains the settings that affect the display of your content.') . '</p>' .
 		'<p>' . sprintf(__('You can choose what&#8217;s displayed on the front page of your site. It can be posts in reverse chronological order (classic blog), or a fixed/static page. To set a static home page, you first need to create two <a href="%s">Pages</a>. One will become the front page, and the other will be where your posts are displayed.'), 'post-new.php?post_type=page') . '</p>' .
-		'<p>' . __('You can also control the display of your content in RSS feeds, including the maximum numbers of posts to display and whether to show full text or a summary.') . '</p>' .
+		'<p>' . __('You can also control the display of your content in RSS feeds, including the maximum number of posts to display and whether to show full text or a summary.') . '</p>' .
 		'<p>' . __('You must click the Save Changes button at the bottom of the screen for new settings to take effect.') . '</p>',
 ) );
 
diff --git a/src/wp-admin/widgets.php b/src/wp-admin/widgets.php
index db4459b..ade84bb 100644
--- a/src/wp-admin/widgets.php
+++ b/src/wp-admin/widgets.php
@@ -387,7 +387,10 @@
 <div id="widgets-left">
 	<div id="available-widgets" class="widgets-holder-wrap">
 		<div class="sidebar-name">
-			<div class="sidebar-name-arrow"><br /></div>
+			<button type="button" class="handlediv button-link" aria-expanded="true">
+				<span class="screen-reader-text">Toggle panel: <?php _e( 'Available Widgets' ); ?></span>
+				<span class="sidebar-name-arrow" aria-hidden="true"></span>
+			</button>
 			<h2><?php _e( 'Available Widgets' ); ?> <span id="removing-widget"><?php _ex( 'Deactivate', 'removing-widget' ); ?> <span></span></span></h2>
 		</div>
 		<div class="widget-holder">
diff --git a/src/wp-content/themes/twentyseventeen/README.txt b/src/wp-content/themes/twentyseventeen/README.txt
index 5f1cfb7..9360fc7 100644
--- a/src/wp-content/themes/twentyseventeen/README.txt
+++ b/src/wp-content/themes/twentyseventeen/README.txt
@@ -2,7 +2,7 @@
 Contributors: the WordPress team
 Requires at least: WordPress 4.7
 Tested up to: WordPress 4.7
-Version: 1.1
+Version: 1.2
 License: GPLv2 or later
 License URI: http://www.gnu.org/licenses/gpl-2.0.html
 Tags: one-column, two-columns, right-sidebar, flexible-header, accessibility-ready, custom-colors, custom-header, custom-menu, custom-logo, editor-style, featured-images, footer-widgets, post-formats, rtl-language-support, sticky-post, theme-options, threaded-comments, translation-ready
@@ -60,13 +60,15 @@ Source: https://unsplash.com/@englr?photo=bIhpiQA009k
 
 == Changelog ==
 
+= 1.2 =
+* Released: April 18, 2017
+
+https://codex.wordpress.org/Twenty_Seventeen_Theme_Changelog#Version_1.2
+
 = 1.1 =
 * Released: January 6, 2017
 
-- Fix incorrect $content_width value in theme
-- Ensure functions in customize-controls.js don't count on Customizer sections always being present
-- Deprecate page_home nav menu item starter content in favor of link_home
-- Introduce a theme-specific filter twentyseventeen_starter_content for customizing the starter content array
+https://codex.wordpress.org/Twenty_Seventeen_Theme_Changelog#Version_1.1
 
 = 1.0 =
 * Released: December 6, 2016
diff --git a/src/wp-content/themes/twentyseventeen/footer.php b/src/wp-content/themes/twentyseventeen/footer.php
index c21cfb7..504f573 100644
--- a/src/wp-content/themes/twentyseventeen/footer.php
+++ b/src/wp-content/themes/twentyseventeen/footer.php
@@ -9,7 +9,7 @@
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
- * @version 1.0
+ * @version 1.2
  */
 
 ?>
diff --git a/src/wp-content/themes/twentyseventeen/style.css b/src/wp-content/themes/twentyseventeen/style.css
index da3791d..37ec33f 100644
--- a/src/wp-content/themes/twentyseventeen/style.css
+++ b/src/wp-content/themes/twentyseventeen/style.css
@@ -4,7 +4,7 @@ Theme URI: https://wordpress.org/themes/twentyseventeen/
 Author: the WordPress team
 Author URI: https://wordpress.org/
 Description: Twenty Seventeen brings your site to life with header video and immersive featured images. With a focus on business sites, it features multiple sections on the front page as well as widgets, navigation and social menus, a logo, and more. Personalize its asymmetrical grid with a custom color scheme and showcase your multimedia content with post formats. Our default theme for 2017 works great in many languages, for any abilities, and on any device.
-Version: 1.1
+Version: 1.2
 License: GNU General Public License v2 or later
 License URI: http://www.gnu.org/licenses/gpl-2.0.html
 Text Domain: twentyseventeen
@@ -451,7 +451,8 @@ h1 {
 	font-weight: 300;
 }
 
-h2 {
+h2,
+.home.blog .entry-title {
 	color: #666;
 	font-size: 20px;
 	font-size: 1.25rem;
@@ -3153,6 +3154,7 @@ object {
 	}
 
 	h2,
+	.home.blog .entry-title,
 	.page .panel-content .recent-posts .entry-title {
 		font-size: 26px;
 		font-size: 1.625rem;
diff --git a/src/wp-content/themes/twentyseventeen/template-parts/navigation/navigation-top.php b/src/wp-content/themes/twentyseventeen/template-parts/navigation/navigation-top.php
index be7e426..2c2115e 100644
--- a/src/wp-content/themes/twentyseventeen/template-parts/navigation/navigation-top.php
+++ b/src/wp-content/themes/twentyseventeen/template-parts/navigation/navigation-top.php
@@ -5,7 +5,7 @@
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
- * @version 1.0
+ * @version 1.2
  */
 
 ?>
diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-audio.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-audio.php
index cf17889..63cf2e5 100644
--- a/src/wp-content/themes/twentyseventeen/template-parts/post/content-audio.php
+++ b/src/wp-content/themes/twentyseventeen/template-parts/post/content-audio.php
@@ -7,7 +7,7 @@
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
- * @version 1.0
+ * @version 1.2
  */
 
 ?>
@@ -33,6 +33,8 @@
 
 			if ( is_single() ) {
 				the_title( '<h1 class="entry-title">', '</h1>' );
+			} elseif ( is_front_page() && is_home() ) {
+				the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
 			} else {
 				the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
 			}
diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-excerpt.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-excerpt.php
index 4ddc28c..a552fba 100644
--- a/src/wp-content/themes/twentyseventeen/template-parts/post/content-excerpt.php
+++ b/src/wp-content/themes/twentyseventeen/template-parts/post/content-excerpt.php
@@ -9,7 +9,7 @@
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
- * @version 1.0
+ * @version 1.2
  */
 
 ?>
@@ -30,7 +30,13 @@
 			</div><!-- .entry-meta -->
 		<?php endif; ?>
 
-		<?php the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' ); ?>
+		<?php if ( is_front_page() && ! is_home() ) {
+
+			// The excerpt is being displayed within a front page section, so it's a lower hierarchy than h2.
+			the_title( sprintf( '<h3 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h3>' );
+		} else {
+			the_title( sprintf( '<h2 class="entry-title"><a href="%s" rel="bookmark">', esc_url( get_permalink() ) ), '</a></h2>' );
+		} ?>
 	</header><!-- .entry-header -->
 
 	<div class="entry-summary">
diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-gallery.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-gallery.php
index e46e7c5..f5934e1 100644
--- a/src/wp-content/themes/twentyseventeen/template-parts/post/content-gallery.php
+++ b/src/wp-content/themes/twentyseventeen/template-parts/post/content-gallery.php
@@ -7,7 +7,7 @@
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
- * @version 1.0
+ * @version 1.2
  */
 
 ?>
@@ -33,6 +33,8 @@
 
 		if ( is_single() ) {
 			the_title( '<h1 class="entry-title">', '</h1>' );
+		} elseif ( is_front_page() && is_home() ) {
+			the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
 		} else {
 			the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
 		}
diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-image.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-image.php
index 8568d92..91c6545 100644
--- a/src/wp-content/themes/twentyseventeen/template-parts/post/content-image.php
+++ b/src/wp-content/themes/twentyseventeen/template-parts/post/content-image.php
@@ -7,7 +7,7 @@
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
- * @version 1.0
+ * @version 1.2
  */
 
 ?>
@@ -33,6 +33,8 @@
 
 			if ( is_single() ) {
 				the_title( '<h1 class="entry-title">', '</h1>' );
+			} elseif ( is_front_page() && is_home() ) {
+				the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
 			} else {
 				the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
 			}
diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content-video.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content-video.php
index 1c3ef49..dad681f 100644
--- a/src/wp-content/themes/twentyseventeen/template-parts/post/content-video.php
+++ b/src/wp-content/themes/twentyseventeen/template-parts/post/content-video.php
@@ -7,7 +7,7 @@
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
- * @version 1.0
+ * @version 1.2
  */
 
 ?>
@@ -33,6 +33,8 @@
 
 			if ( is_single() ) {
 				the_title( '<h1 class="entry-title">', '</h1>' );
+			} elseif ( is_front_page() && is_home() ) {
+				the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
 			} else {
 				the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
 			}
diff --git a/src/wp-content/themes/twentyseventeen/template-parts/post/content.php b/src/wp-content/themes/twentyseventeen/template-parts/post/content.php
index a87f646..90c3a4d 100644
--- a/src/wp-content/themes/twentyseventeen/template-parts/post/content.php
+++ b/src/wp-content/themes/twentyseventeen/template-parts/post/content.php
@@ -7,7 +7,7 @@
  * @package WordPress
  * @subpackage Twenty_Seventeen
  * @since 1.0
- * @version 1.0
+ * @version 1.2
  */
 
 ?>
@@ -33,6 +33,8 @@
 
 		if ( is_single() ) {
 			the_title( '<h1 class="entry-title">', '</h1>' );
+		} elseif ( is_front_page() && is_home() ) {
+			the_title( '<h3 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h3>' );
 		} else {
 			the_title( '<h2 class="entry-title"><a href="' . esc_url( get_permalink() ) . '" rel="bookmark">', '</a></h2>' );
 		}
diff --git a/src/wp-includes/capabilities.php b/src/wp-includes/capabilities.php
index 0e766b1..abd87257 100644
--- a/src/wp-includes/capabilities.php
+++ b/src/wp-includes/capabilities.php
@@ -478,6 +478,7 @@ function map_meta_cap( $cap, $user_id ) {
 	case 'manage_network_plugins':
 	case 'manage_network_themes':
 	case 'manage_network_options':
+	case 'upgrade_network':
 		$caps[] = $cap;
 		break;
 	case 'setup_network':
diff --git a/src/wp-includes/class-walker-nav-menu.php b/src/wp-includes/class-walker-nav-menu.php
index 4c1a1f2..c97188b 100644
--- a/src/wp-includes/class-walker-nav-menu.php
+++ b/src/wp-includes/class-walker-nav-menu.php
@@ -58,7 +58,22 @@ public function start_lvl( &$output, $depth = 0, $args = array() ) {
 			$n = "\n";
 		}
 		$indent = str_repeat( $t, $depth );
-		$output .= "{$n}{$indent}<ul class=\"sub-menu\">{$n}";
+
+		/**
+		 * Filters the CSS class(es) applied to a menu list element.
+		 *
+		 * @since 4.8.0
+		 *
+		 * @param array    $classes The CSS classes that are applied to the menu `<ul>` element.
+		 * @param stdClass $args    An object of `wp_nav_menu()` arguments.
+		 * @param int      $depth   Depth of menu item. Used for padding.
+		 */
+
+		$classes = array( 'sub-menu' );
+		$classes = apply_filters( 'nav_menu_submenu_css_class', $classes, $args, $depth );
+		$class_names = join( ' ', $classes );
+
+		$output .= "{$n}{$indent}<ul class='" . esc_attr( $class_names ) . "'>{$n}";
 	}
 
 	/**
diff --git a/src/wp-includes/class-walker-page.php b/src/wp-includes/class-walker-page.php
index 30831bb..8bec66f 100644
--- a/src/wp-includes/class-walker-page.php
+++ b/src/wp-includes/class-walker-page.php
@@ -160,10 +160,38 @@ public function start_el( &$output, $page, $depth = 0, $args = array(), $current
 		$args['link_before'] = empty( $args['link_before'] ) ? '' : $args['link_before'];
 		$args['link_after'] = empty( $args['link_after'] ) ? '' : $args['link_after'];
 
+		$atts = array();
+		$atts['href'] = get_permalink( $page->ID );
+
+		/**
+		 * Filters the HTML attributes applied to a page menu item's anchor element.
+		 *
+		 * @since 4.8.0
+		 *
+		 * @param array $atts {
+		 *     The HTML attributes applied to the menu item's `<a>` element, empty strings are ignored.
+		 *
+		 *     @type string $href The href attribute.
+		 * }
+		 * @param WP_Post $page         Page data object.
+		 * @param int     $depth        Depth of page, used for padding.
+		 * @param array   $args         An array of arguments.
+		 * @param int     $current_page ID of the current page.
+		 */
+		$atts = apply_filters( 'page_menu_link_attributes', $atts, $page, $depth, $args, $current_page );
+
+		$attributes = '';
+		foreach ( $atts as $attr => $value ) {
+			if ( ! empty( $value ) ) {
+				$value = esc_attr( $value );
+				$attributes .= ' ' . $attr . '="' . $value . '"';
+			}
+		}
+
 		$output .= $indent . sprintf(
-			'<li class="%s"><a href="%s">%s%s%s</a>',
+			'<li class="%s"><a%s>%s%s%s</a>',
 			$css_classes,
-			get_permalink( $page->ID ),
+			$attributes,
 			$args['link_before'],
 			/** This filter is documented in wp-includes/post-template.php */
 			apply_filters( 'the_title', $page->post_title, $page->ID ),
diff --git a/src/wp-includes/class-wp-editor.php b/src/wp-includes/class-wp-editor.php
index 83777cf..21353c6 100644
--- a/src/wp-includes/class-wp-editor.php
+++ b/src/wp-includes/class-wp-editor.php
@@ -27,6 +27,8 @@
 	private static $drag_drop_upload = false;
 	private static $old_dfw_compat = false;
 	private static $translation;
+	private static $tinymce_scripts_printed = false;
+	private static $link_dialog_printed = false;
 
 	private function __construct() {}
 
@@ -350,21 +352,9 @@ public static function editor_settings($editor_id, $set) {
 		if ( self::$this_tinymce ) {
 
 			if ( empty( self::$first_init ) ) {
-				self::$baseurl = includes_url( 'js/tinymce' );
-
-				$mce_locale = get_user_locale();
-				self::$mce_locale = $mce_locale = empty( $mce_locale ) ? 'en' : strtolower( substr( $mce_locale, 0, 2 ) ); // ISO 639-1
-
-				/** This filter is documented in wp-admin/includes/media.php */
-				$no_captions = (bool) apply_filters( 'disable_captions', '' );
+				$baseurl = self::get_baseurl();
+				$mce_locale = self::get_mce_locale();
 				$ext_plugins = '';
-				$shortcut_labels = array();
-
-				foreach ( self::get_translation() as $name => $value ) {
-					if ( is_array( $value ) ) {
-						$shortcut_labels[$name] = $value[1];
-					}
-				}
 
 				if ( $set['teeny'] ) {
 
@@ -376,7 +366,7 @@ public static function editor_settings($editor_id, $set) {
 					 * @param array  $plugins   An array of teenyMCE plugins.
 					 * @param string $editor_id Unique editor identifier, e.g. 'content'.
 					 */
-					self::$plugins = $plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
+					$plugins = apply_filters( 'teeny_mce_plugins', array( 'colorpicker', 'lists', 'fullscreen', 'image', 'wordpress', 'wpeditimage', 'wplink' ), $editor_id );
 				} else {
 
 					/**
@@ -521,65 +511,23 @@ public static function editor_settings($editor_id, $set) {
 				self::$plugins = $plugins;
 				self::$ext_plugins = $ext_plugins;
 
-				self::$first_init = array(
-					'theme' => 'modern',
-					'skin' => 'lightgray',
-					'language' => self::$mce_locale,
-					'formats' => '{' .
-						'alignleft: [' .
-							'{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"left"}},' .
-							'{selector: "img,table,dl.wp-caption", classes: "alignleft"}' .
-						'],' .
-						'aligncenter: [' .
-							'{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"center"}},' .
-							'{selector: "img,table,dl.wp-caption", classes: "aligncenter"}' .
-						'],' .
-						'alignright: [' .
-							'{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"right"}},' .
-							'{selector: "img,table,dl.wp-caption", classes: "alignright"}' .
-						'],' .
-						'strikethrough: {inline: "del"}' .
-					'}',
-					'relative_urls' => false,
-					'remove_script_host' => false,
-					'convert_urls' => false,
-					'browser_spellcheck' => true,
-					'fix_list_elements' => true,
-					'entities' => '38,amp,60,lt,62,gt',
-					'entity_encoding' => 'raw',
-					'keep_styles' => false,
-					'cache_suffix' => 'wp-mce-' . $tinymce_version,
-
-					// Limit the preview styles in the menu/toolbar
-					'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform',
-
-					'end_container_on_empty_block' => true,
-					'wpeditimage_disable_captions' => $no_captions,
-					'wpeditimage_html5_captions' => current_theme_supports( 'html5', 'caption' ),
-					'plugins' => implode( ',', $plugins ),
-					'wp_lang_attr' => get_bloginfo( 'language' ),
-					'wp_shortcut_labels' => wp_json_encode( $shortcut_labels ),
-				);
+				$settings = self::default_settings();
+				$settings['plugins'] = implode( ',', $plugins );
 
 				if ( ! empty( $mce_external_plugins ) ) {
-					self::$first_init['external_plugins'] = wp_json_encode( $mce_external_plugins );
+					$settings['external_plugins'] = wp_json_encode( $mce_external_plugins );
 				}
 
-				$suffix = SCRIPT_DEBUG ? '' : '.min';
-				$version = 'ver=' . get_bloginfo( 'version' );
-				$dashicons = includes_url( "css/dashicons$suffix.css?$version" );
-
-				// WordPress default stylesheet and dashicons
-				$mce_css = array(
-					$dashicons,
-					self::$baseurl . '/skins/wordpress/wp-content.css?' . $version
-				);
+				/** This filter is documented in wp-admin/includes/media.php */
+				if ( apply_filters( 'disable_captions', '' ) ) {
+					$settings['wpeditimage_disable_captions'] = true;
+				}
 
+				$mce_css = $settings['content_css'];
 				$editor_styles = get_editor_stylesheets();
+
 				if ( ! empty( $editor_styles ) ) {
-					foreach ( $editor_styles as $style ) {
-						$mce_css[] = $style;
-					}
+					$mce_css .= ',' . implode( ',', $editor_styles );
 				}
 
 				/**
@@ -589,10 +537,15 @@ public static function editor_settings($editor_id, $set) {
 				 *
 				 * @param string $stylesheets Comma-delimited list of stylesheets.
 				 */
-				$mce_css = trim( apply_filters( 'mce_css', implode( ',', $mce_css ) ), ' ,' );
+				$mce_css = trim( apply_filters( 'mce_css', $mce_css ), ' ,' );
 
-				if ( ! empty($mce_css) )
-					self::$first_init['content_css'] = $mce_css;
+				if ( ! empty( $mce_css ) ) {
+					$settings['content_css'] = $mce_css;
+				} else {
+					unset( $settings['content_css'] );
+				}
+
+				self::$first_init = $settings;
 			}
 
 			if ( $set['teeny'] ) {
@@ -690,21 +643,19 @@ public static function editor_settings($editor_id, $set) {
 
 			$body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) );
 
-			if ( !empty($set['tinymce']['body_class']) ) {
+			if ( ! empty( $set['tinymce']['body_class'] ) ) {
 				$body_class .= ' ' . $set['tinymce']['body_class'];
-				unset($set['tinymce']['body_class']);
+				unset( $set['tinymce']['body_class'] );
 			}
 
 			$mceInit = array (
 				'selector' => "#$editor_id",
-				'resize' => 'vertical',
-				'menubar' => false,
 				'wpautop' => (bool) $set['wpautop'],
 				'indent' => ! $set['wpautop'],
-				'toolbar1' => implode($mce_buttons, ','),
-				'toolbar2' => implode($mce_buttons_2, ','),
-				'toolbar3' => implode($mce_buttons_3, ','),
-				'toolbar4' => implode($mce_buttons_4, ','),
+				'toolbar1' => implode( ',', $mce_buttons ),
+				'toolbar2' => implode( ',', $mce_buttons_2 ),
+				'toolbar3' => implode( ',', $mce_buttons_3 ),
+				'toolbar4' => implode( ',', $mce_buttons_4 ),
 				'tabfocus_elements' => $set['tabfocus_elements'],
 				'body_class' => $body_class
 			);
@@ -762,19 +713,23 @@ public static function editor_settings($editor_id, $set) {
 	 * @param array $init
 	 * @return string
 	 */
-	private static function _parse_init($init) {
+	private static function _parse_init( $init ) {
 		$options = '';
 
-		foreach ( $init as $k => $v ) {
-			if ( is_bool($v) ) {
-				$val = $v ? 'true' : 'false';
-				$options .= $k . ':' . $val . ',';
+		foreach ( $init as $key => $value ) {
+			if ( is_bool( $value ) ) {
+				$val = $value ? 'true' : 'false';
+				$options .= $key . ':' . $val . ',';
 				continue;
-			} elseif ( !empty($v) && is_string($v) && ( ('{' == $v{0} && '}' == $v{strlen($v) - 1}) || ('[' == $v{0} && ']' == $v{strlen($v) - 1}) || preg_match('/^\(?function ?\(/', $v) ) ) {
-				$options .= $k . ':' . $v . ',';
+			} elseif ( ! empty( $value ) && is_string( $value ) && (
+				( '{' == $value{0} && '}' == $value{strlen( $value ) - 1} ) ||
+				( '[' == $value{0} && ']' == $value{strlen( $value ) - 1} ) ||
+				preg_match( '/^\(?function ?\(/', $value ) ) ) {
+
+				$options .= $key . ':' . $value . ',';
 				continue;
 			}
-			$options .= $k . ':"' . $v . '",';
+			$options .= $key . ':"' . $value . '",';
 		}
 
 		return '{' . trim( $options, ' ,' ) . '}';
@@ -784,28 +739,31 @@ private static function _parse_init($init) {
 	 *
 	 * @static
 	 */
-	public static function enqueue_scripts() {
-		if ( self::$has_tinymce )
-			wp_enqueue_script('editor');
+	public static function enqueue_scripts( $default_scripts = false ) {
+		if ( $default_scripts || self::$has_tinymce ) {
+			wp_enqueue_script( 'editor' );
+		}
 
-		if ( self::$has_quicktags ) {
+		if ( $default_scripts || self::$has_quicktags ) {
 			wp_enqueue_script( 'quicktags' );
 			wp_enqueue_style( 'buttons' );
 		}
 
-		if ( in_array('wplink', self::$plugins, true) || in_array('link', self::$qt_buttons, true) ) {
-			wp_enqueue_script('wplink');
+		if ( $default_scripts || in_array( 'wplink', self::$plugins, true ) || in_array( 'link', self::$qt_buttons, true ) ) {
+			wp_enqueue_script( 'wplink' );
 			wp_enqueue_script( 'jquery-ui-autocomplete' );
 		}
 
 		if ( self::$old_dfw_compat ) {
-			wp_enqueue_script('wp-fullscreen-stub');
+			wp_enqueue_script( 'wp-fullscreen-stub' );
 		}
 
 		if ( self::$has_medialib ) {
 			add_thickbox();
 			wp_enqueue_script( 'media-upload' );
 			wp_enqueue_script( 'wp-embed' );
+		} elseif ( $default_scripts ) {
+			wp_enqueue_script( 'media-upload' );
 		}
 
 		/**
@@ -817,9 +775,196 @@ public static function enqueue_scripts() {
 		 *                       and Quicktags are being loaded.
 		 */
 		do_action( 'wp_enqueue_editor', array(
-			'tinymce'   => self::$has_tinymce,
-			'quicktags' => self::$has_quicktags,
+			'tinymce'   => ( $default_scripts || self::$has_tinymce ),
+			'quicktags' => ( $default_scripts || self::$has_quicktags ),
+		) );
+	}
+
+	/**
+	 * Enqueue all editor scripts.
+	 * For use when the editor is going to be initialized after page load.
+	 *
+	 * @since 4.8.0
+	 */
+	public static function enqueue_default_editor() {
+		// We are past the point where scripts can be enqueued properly.
+		if ( did_action( 'wp_enqueue_editor' ) ) {
+			return;
+		}
+
+		self::enqueue_scripts( true );
+
+		// Also add wp-includes/css/editor.css
+		wp_enqueue_style( 'editor-buttons' );
+
+		if ( is_admin() ) {
+			add_action( 'admin_print_footer_scripts', array( __CLASS__, 'print_default_editor_scripts' ), 45 );
+		} else {
+			add_action( 'wp_print_footer_scripts', array( __CLASS__, 'print_default_editor_scripts' ), 45 );
+		}
+	}
+
+	/**
+	 * Print (output) all editor scripts and default settings.
+	 * For use when the editor is going to be initialized after page load.
+	 *
+	 * @since 4.8.0
+	 *
+	 */
+	public static function print_default_editor_scripts() {
+		$settings = self::default_settings();
+
+		$settings['toolbar1'] = 'bold,italic,bullist,numlist,link';
+		$settings['wpautop'] = false;
+		$settings['indent'] = true;
+		$settings['elementpath'] = false;
+
+		// In production all plugins are loaded (they are in wp-editor.js.gz)
+		// but only these will be initialized by default.
+		$settings['plugins'] = implode( ',', array(
+			'charmap',
+			'colorpicker',
+			'hr',
+			'lists',
+	//		'media',
+			'paste',
+			'tabfocus',
+			'textcolor',
+			'fullscreen',
+			'wordpress',
+			'wpautoresize',
+			'wpeditimage',
+			'wpemoji',
+			'wpgallery',
+			'wplink',
+	//		'wpdialogs',
+			'wptextpattern',
+	//		'wpview',
 		) );
+
+		$settings = self::_parse_init( $settings );
+
+		$suffix = SCRIPT_DEBUG ? '' : '.min';
+		$baseurl = self::get_baseurl();
+
+		?>
+		<script type="text/javascript">
+		window.wp = window.wp || {};
+		window.wp.editor = window.wp.editor || {};
+		window.wp.editor.getDefaultSettings = function() {
+			return {
+				tinymce: <?php echo $settings; ?>,
+				quicktags: {
+					buttons: 'strong,em,link,ul,ol,li,code'
+				}
+			};
+		};
+
+		var tinyMCEPreInit = {
+			baseURL: "<?php echo $baseurl; ?>",
+			suffix: "<?php echo $suffix; ?>",
+			mceInit: {},
+			qtInit: {},
+			load_ext: function(url,lang){var sl=tinymce.ScriptLoader;sl.markDone(url+'/langs/'+lang+'.js');sl.markDone(url+'/langs/'+lang+'_dlg.js');}
+		};
+		</script>
+		<?php
+
+		self::print_tinymce_scripts();
+
+		/**
+		 * Fires when the editor scripts are loaded for later initialization,
+		 * after all scripts and settings are printed.
+		 *
+		 * @since 4.8.0
+		 */
+		do_action( 'print_default_editor_scripts' );
+
+		self::wp_link_dialog();
+	}
+
+	public static function get_mce_locale() {
+		if ( empty( self::$mce_locale ) ) {
+			$mce_locale = get_user_locale();
+			self::$mce_locale = empty( $mce_locale ) ? 'en' : strtolower( substr( $mce_locale, 0, 2 ) ); // ISO 639-1
+		}
+
+		return self::$mce_locale;
+	}
+
+	public static function get_baseurl() {
+		if ( empty( self::$baseurl ) ) {
+			self::$baseurl = includes_url( 'js/tinymce' );
+		}
+
+		return self::$baseurl;
+	}
+
+	/**
+	 * Returns the default TinyMCE settings.
+	 * Doesn't include plugins, buttons, editor selector.
+	 *
+	 * @return array
+	 */
+	private static function default_settings() {
+		global $tinymce_version;
+
+		$shortcut_labels = array();
+
+		foreach ( self::get_translation() as $name => $value ) {
+			if ( is_array( $value ) ) {
+				$shortcut_labels[$name] = $value[1];
+			}
+		}
+
+		$settings = array(
+			'theme' => 'modern',
+			'skin' => 'lightgray',
+			'language' => self::get_mce_locale(),
+			'formats' => '{' .
+				'alignleft: [' .
+					'{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"left"}},' .
+					'{selector: "img,table,dl.wp-caption", classes: "alignleft"}' .
+				'],' .
+				'aligncenter: [' .
+					'{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"center"}},' .
+					'{selector: "img,table,dl.wp-caption", classes: "aligncenter"}' .
+				'],' .
+				'alignright: [' .
+					'{selector: "p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li", styles: {textAlign:"right"}},' .
+					'{selector: "img,table,dl.wp-caption", classes: "alignright"}' .
+				'],' .
+				'strikethrough: {inline: "del"}' .
+			'}',
+			'relative_urls' => false,
+			'remove_script_host' => false,
+			'convert_urls' => false,
+			'browser_spellcheck' => true,
+			'fix_list_elements' => true,
+			'entities' => '38,amp,60,lt,62,gt',
+			'entity_encoding' => 'raw',
+			'keep_styles' => false,
+			'cache_suffix' => 'wp-mce-' . $tinymce_version,
+			'resize' => 'vertical',
+			'menubar' => false,
+
+			// Limit the preview styles in the menu/toolbar
+			'preview_styles' => 'font-family font-size font-weight font-style text-decoration text-transform',
+
+			'end_container_on_empty_block' => true,
+			'wpeditimage_html5_captions' => true,
+			'wp_lang_attr' => get_bloginfo( 'language' ),
+			'wp_shortcut_labels' => wp_json_encode( $shortcut_labels ),
+		);
+
+		$suffix = SCRIPT_DEBUG ? '' : '.min';
+		$version = 'ver=' . get_bloginfo( 'version' );
+
+		// Default stylesheets
+		$settings['content_css'] = includes_url( "css/dashicons$suffix.css?$version" ) . ',' .
+			includes_url( "js/tinymce/skins/wordpress/wp-content.css?$version" );
+
+		return $settings;
 	}
 
 	private static function get_translation() {
@@ -1028,11 +1173,16 @@ private static function get_translation() {
 
 			/* translators: word count */
 			'Words: {0}' => sprintf( __( 'Words: %s' ), '{0}' ),
-			'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' => __( 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' ) . "\n\n" . __( 'If you&#8217;re looking to paste rich content from Microsoft Word, try turning this option off. The editor will clean up text pasted from Word automatically.' ),
-			'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' => __( 'Rich Text Area. Press Alt-Shift-H for help.' ),
+			'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' =>
+				__( 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.' ) . "\n\n" .
+				__( 'If you&#8217;re looking to paste rich content from Microsoft Word, try turning this option off. The editor will clean up text pasted from Word automatically.' ),
+			'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help' =>
+				__( 'Rich Text Area. Press Alt-Shift-H for help.' ),
 			'Rich Text Area. Press Control-Option-H for help.' => __( 'Rich Text Area. Press Control-Option-H for help.' ),
-			'You have unsaved changes are you sure you want to navigate away?' => __( 'The changes you made will be lost if you navigate away from this page.' ),
-			'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' => __( 'Your browser does not support direct access to the clipboard. Please use keyboard shortcuts or your browser&#8217;s edit menu instead.' ),
+			'You have unsaved changes are you sure you want to navigate away?' =>
+				__( 'The changes you made will be lost if you navigate away from this page.' ),
+			'Your browser doesn\'t support direct access to the clipboard. Please use the Ctrl+X/C/V keyboard shortcuts instead.' =>
+				__( 'Your browser does not support direct access to the clipboard. Please use keyboard shortcuts or your browser&#8217;s edit menu instead.' ),
 
 			// TinyMCE menus
 			'Insert' => _x( 'Insert', 'TinyMCE menu' ),
@@ -1055,6 +1205,8 @@ private static function get_translation() {
 			'Paste URL or type to search' => __( 'Paste URL or type to search' ), // Placeholder for the inline link dialog
 			'Apply'  => __( 'Apply' ), // Tooltip for the 'apply' button in the inline link dialog
 			'Link options'  => __( 'Link options' ), // Tooltip for the 'link options' button in the inline link dialog
+			'Visual' => __( 'Visual' ), // Editor switch tab label
+			'Text' => __( 'Text' ), // Editor switch tab label
 
 			// Shortcuts help modal
 			'Keyboard Shortcuts' => array( __( 'Keyboard Shortcuts' ), 'accessH' ),
@@ -1098,8 +1250,8 @@ private static function get_translation() {
 	}
 
 	/**
-	 * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n().
-	 * Can be used directly (_WP_Editors::wp_mce_translation()) by passing the same locale as set in the TinyMCE init object.
+	 * Translates the default TinyMCE strings and returns them as JSON encoded object ready to be loaded with tinymce.addI18n(),
+	 * or as JS snippet that should run after tinymce.js is loaded.
 	 *
 	 * @static
 	 * @param string $mce_locale The locale used for the editor.
@@ -1108,11 +1260,11 @@ private static function get_translation() {
 	 */
 	public static function wp_mce_translation( $mce_locale = '', $json_only = false ) {
 		if ( ! $mce_locale ) {
-			$mce_locale = self::$mce_locale;
+			$mce_locale = self::get_mce_locale();
 		}
 
 		$mce_translation = self::get_translation();
-		
+
 		foreach ( $mce_translation as $name => $value ) {
 			if ( is_array( $value ) ) {
 				$mce_translation[$name] = $value[0];
@@ -1150,55 +1302,82 @@ public static function wp_mce_translation( $mce_locale = '', $json_only = false
 			return wp_json_encode( $mce_translation );
 		}
 
-		$baseurl = self::$baseurl ? self::$baseurl : includes_url( 'js/tinymce' );
+		$baseurl = self::get_baseurl();
 
 		return "tinymce.addI18n( '$mce_locale', " . wp_json_encode( $mce_translation ) . ");\n" .
 			"tinymce.ScriptLoader.markDone( '$baseurl/langs/$mce_locale.js' );\n";
 	}
 
 	/**
+	 * Print (output) the main TinyMCE scripts.
+	 *
+	 * @since 4.8
 	 *
 	 * @static
 	 * @global string $tinymce_version
 	 * @global bool   $concatenate_scripts
 	 * @global bool   $compress_scripts
 	 */
-	public static function editor_js() {
+	public static function print_tinymce_scripts() {
 		global $tinymce_version, $concatenate_scripts, $compress_scripts;
 
-		/**
-		 * Filters "tiny_mce_version" is deprecated
-		 *
-		 * The tiny_mce_version filter is not needed since external plugins are loaded directly by TinyMCE.
-		 * These plugins can be refreshed by appending query string to the URL passed to "mce_external_plugins" filter.
-		 * If the plugin has a popup dialog, a query string can be added to the button action that opens it (in the plugin's code).
-		 */
-		$version = 'ver=' . $tinymce_version;
-		$tmce_on = !empty(self::$mce_settings);
+		if ( self::$tinymce_scripts_printed ) {
+			return;
+		}
+
+		self::$tinymce_scripts_printed = true;
 
-		if ( ! isset($concatenate_scripts) )
+		if ( ! isset( $concatenate_scripts ) ) {
 			script_concat_settings();
+		}
+
+		$suffix = SCRIPT_DEBUG ? '' : '.min';
+		$version = 'ver=' . $tinymce_version;
+		$baseurl = self::get_baseurl();
 
 		$compressed = $compress_scripts && $concatenate_scripts && isset($_SERVER['HTTP_ACCEPT_ENCODING'])
 			&& false !== stripos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip');
 
+		// Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG)
+		$mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min';
+
+		if ( $compressed ) {
+			echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
+		} else {
+			echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n";
+			echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
+		}
+
+		echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
+	}
+
+	/**
+	 * Print (output) the TinyMCE configuration and initialization scripts.
+	 *
+	 * @static
+	 */
+	public static function editor_js() {
+		global $tinymce_version;
+
+		$tmce_on = ! empty( self::$mce_settings );
 		$mceInit = $qtInit = '';
+
 		if ( $tmce_on ) {
 			foreach ( self::$mce_settings as $editor_id => $init ) {
 				$options = self::_parse_init( $init );
 				$mceInit .= "'$editor_id':{$options},";
 			}
-			$mceInit = '{' . trim($mceInit, ',') . '}';
+			$mceInit = '{' . trim( $mceInit, ',' ) . '}';
 		} else {
 			$mceInit = '{}';
 		}
 
-		if ( !empty(self::$qt_settings) ) {
+		if ( ! empty( self::$qt_settings ) ) {
 			foreach ( self::$qt_settings as $editor_id => $init ) {
 				$options = self::_parse_init( $init );
 				$qtInit .= "'$editor_id':{$options},";
 			}
-			$qtInit = '{' . trim($qtInit, ',') . '}';
+			$qtInit = '{' . trim( $qtInit, ',' ) . '}';
 		} else {
 			$qtInit = '{}';
 		}
@@ -1210,6 +1389,8 @@ public static function editor_js() {
 		);
 
 		$suffix = SCRIPT_DEBUG ? '' : '.min';
+		$baseurl = self::get_baseurl();
+		$version = 'ver=' . $tinymce_version;
 
 		/**
 		 * Fires immediately before the TinyMCE settings are printed.
@@ -1223,7 +1404,7 @@ public static function editor_js() {
 
 		<script type="text/javascript">
 		tinyMCEPreInit = {
-			baseURL: "<?php echo self::$baseurl; ?>",
+			baseURL: "<?php echo $baseurl; ?>",
 			suffix: "<?php echo $suffix; ?>",
 			<?php
 
@@ -1240,19 +1421,8 @@ public static function editor_js() {
 		</script>
 		<?php
 
-		$baseurl = self::$baseurl;
-		// Load tinymce.js when running from /src, else load wp-tinymce.js.gz (production) or tinymce.min.js (SCRIPT_DEBUG)
-		$mce_suffix = false !== strpos( get_bloginfo( 'version' ), '-src' ) ? '' : '.min';
-
 		if ( $tmce_on ) {
-			if ( $compressed ) {
-				echo "<script type='text/javascript' src='{$baseurl}/wp-tinymce.php?c=1&amp;$version'></script>\n";
-			} else {
-				echo "<script type='text/javascript' src='{$baseurl}/tinymce{$mce_suffix}.js?$version'></script>\n";
-				echo "<script type='text/javascript' src='{$baseurl}/plugins/compat3x/plugin{$suffix}.js?$version'></script>\n";
-			}
-
-			echo "<script type='text/javascript'>\n" . self::wp_mce_translation() . "</script>\n";
+			self::print_tinymce_scripts();
 
 			if ( self::$ext_plugins ) {
 				// Load the old-format English strings to prevent unsightly labels in old style popups
@@ -1434,6 +1604,13 @@ public static function wp_link_query( $args = array() ) {
 	 * @static
 	 */
 	public static function wp_link_dialog() {
+		// Run once
+		if ( self::$link_dialog_printed ) {
+			return;
+		}
+
+		self::$link_dialog_printed = true;
+
 		// display: none is required here, see #WP27605
 		?>
 		<div id="wp-link-backdrop" style="display: none"></div>
diff --git a/src/wp-includes/class-wp-site.php b/src/wp-includes/class-wp-site.php
index c16f498..4bca299 100644
--- a/src/wp-includes/class-wp-site.php
+++ b/src/wp-includes/class-wp-site.php
@@ -240,11 +240,15 @@ public function __get( $key ) {
 			case 'siteurl':
 			case 'post_count':
 			case 'home':
+			default: // Custom properties added by 'site_details' filter.
 				if ( ! did_action( 'ms_loaded' ) ) {
 					return null;
 				}
+
 				$details = $this->get_details();
-				return $details->$key;
+				if ( isset( $details->$key ) ) {
+					return $details->$key;
+				}
 		}
 
 		return null;
@@ -275,6 +279,15 @@ public function __isset( $key ) {
 					return false;
 				}
 				return true;
+			default: // Custom properties added by 'site_details' filter.
+				if ( ! did_action( 'ms_loaded' ) ) {
+					return false;
+				}
+
+				$details = $this->get_details();
+				if ( isset( $details->$key ) ) {
+					return true;
+				}
 		}
 
 		return false;
diff --git a/src/wp-includes/css/admin-bar.css b/src/wp-includes/css/admin-bar.css
index fff096d..523b5f7 100644
--- a/src/wp-includes/css/admin-bar.css
+++ b/src/wp-includes/css/admin-bar.css
@@ -624,12 +624,16 @@ html:lang(he-il) .rtl #wpadminbar *  {
 	-moz-osx-font-smoothing: grayscale;
 }
 
+/* The admin bar search field needs to reset many styles that might be inherited from the active Theme CSS. See ticket #40313. */
 #wpadminbar > #wp-toolbar > #wp-admin-bar-top-secondary > #wp-admin-bar-search #adminbarsearch input.adminbar-input {
+	display: inline-block;
+	float: none;
 	position: relative;
 	z-index: 30;
 	font-size: 13px;
 	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
 	line-height: 24px;
+	text-indent: 0;
 	height: 24px;
 	width: 24px;
 	max-width: none;
diff --git a/src/wp-includes/css/buttons.css b/src/wp-includes/css/buttons.css
index 1a614cb..80ecd07 100644
--- a/src/wp-includes/css/buttons.css
+++ b/src/wp-includes/css/buttons.css
@@ -242,7 +242,7 @@ TABLE OF CONTENTS:
 
 .wp-core-ui .button-link-delete:hover,
 .wp-core-ui .button-link-delete:focus {
-	color: #f00;
+	color: #dc3232;
 }
 
 .ie8 .wp-core-ui .button-link:focus {
@@ -411,6 +411,11 @@ TABLE OF CONTENTS:
 		vertical-align: inherit;
 	}
 
+	.media-modal-content .media-toolbar-primary .media-button {
+		margin-top: 10px;
+		margin-left: 5px;
+	}
+
 	/* Reset responsive styles on Log in button on iframed login form */
 
 	.interim-login .button.button-large {
diff --git a/src/wp-includes/css/editor.css b/src/wp-includes/css/editor.css
index e04317e..ca23b91 100644
--- a/src/wp-includes/css/editor.css
+++ b/src/wp-includes/css/editor.css
@@ -608,16 +608,12 @@ div.mce-path {
 }
 
 /* Menubar */
-.mce-menubar {
+div.mce-menubar {
 	border-color: #e5e5e5;
 	background: #fff;
 	border-width: 0px 0px 1px;
 }
 
-.mce-menubar .mce-menubtn {
-	margin: 2px;
-}
-
 .mce-menubar .mce-menubtn:hover,
 .mce-menubar .mce-menubtn.mce-active,
 .mce-menubar .mce-menubtn:focus {
@@ -1091,6 +1087,7 @@ i.mce-i-wp_code:before {
 
 .wp-editor-container {
 	clear: both;
+	border: 1px solid #e5e5e5;
 }
 
 .wp-editor-area {
diff --git a/src/wp-includes/css/media-views.css b/src/wp-includes/css/media-views.css
index 698ea2d..0c0bd6d 100644
--- a/src/wp-includes/css/media-views.css
+++ b/src/wp-includes/css/media-views.css
@@ -242,14 +242,6 @@
 	width: calc(48% - 12px);
 }
 
-.media-modal-content .attachments-browser .media-toolbar-secondary {
-	width: 66%;
-}
-
-.media-modal-content .media-toolbar-primary.search-form {
-	width: 33%;
-}
-
 .media-modal-content .media-toolbar-primary .media-button {
 	float: right;
 }
@@ -279,6 +271,20 @@
 	overflow: hidden;
 }
 
+.media-frame-toolbar .media-toolbar {
+	top: initial;
+	bottom: -45px;
+	height: auto;
+	overflow: initial;
+	border-top: 1px solid #ddd;
+}
+
+@media screen and (max-width: 782px) {
+	.media-frame-toolbar .media-toolbar {
+		bottom: -48px;
+	}
+}
+
 .media-toolbar-primary {
 	float: right;
 	height: 100%;
@@ -686,7 +692,6 @@
 	overflow: auto;
 	background: #fff;
 	border-top: 1px solid #ddd;
-	border-bottom: 1px solid #ddd;
 }
 
 .media-frame-toolbar {
@@ -696,6 +701,8 @@
 	bottom: 0;
 	height: 60px;
 	z-index: 100;
+	bottom: 60px;
+	height: auto;
 }
 
 .media-frame.hide-menu .media-frame-title,
diff --git a/src/wp-includes/customize/class-wp-customize-color-control.php b/src/wp-includes/customize/class-wp-customize-color-control.php
index 7f6a9b5..775a63d 100644
--- a/src/wp-includes/customize/class-wp-customize-color-control.php
+++ b/src/wp-includes/customize/class-wp-customize-color-control.php
@@ -92,7 +92,7 @@ public function render_content() {}
 	 */
 	public function content_template() {
 		?>
-		<# var defaultValue = '',
+		<# var defaultValue = '#RRGGBB', defaultValueAttr = '',
 			isHueSlider = data.mode === 'hue';
 
 		if ( data.defaultValue && ! isHueSlider ) {
@@ -101,7 +101,7 @@ public function content_template() {
 			} else {
 				defaultValue = data.defaultValue;
 			}
-			defaultValue = ' data-default-color=' + defaultValue; // Quotes added automatically.
+			defaultValueAttr = ' data-default-color=' + defaultValue; // Quotes added automatically.
 		} #>
 		<label>
 			<# if ( data.label ) { #>
@@ -114,7 +114,7 @@ public function content_template() {
 				<# if ( isHueSlider ) { #>
 					<input class="color-picker-hue" type="text" data-type="hue" />
 				<# } else { #>
-					<input class="color-picker-hex" type="text" maxlength="7" placeholder="<?php esc_attr_e( 'Hex Value' ); ?>" {{ defaultValue }} />
+					<input class="color-picker-hex" type="text" maxlength="7" placeholder="{{ defaultValue }}" {{ defaultValueAttr }} />
 				<# } #>
 			</div>
 		</label>
diff --git a/src/wp-includes/general-template.php b/src/wp-includes/general-template.php
index 04df698..2930c54 100644
--- a/src/wp-includes/general-template.php
+++ b/src/wp-includes/general-template.php
@@ -3057,6 +3057,23 @@ function wp_editor( $content, $editor_id, $settings = array() ) {
 }
 
 /**
+ * Outputs the editor scripts, stylesheets, and default settings.
+ *
+ * The editor can be initialized when needed after page load.
+ * See wp.editor.initialize() in wp-admin/js/editor.js for initialization options.
+ *
+ * @uses _WP_Editors
+ * @since 4.8.0
+ */
+function wp_enqueue_editor() {
+	if ( ! class_exists( '_WP_Editors', false ) ) {
+		require( ABSPATH . WPINC . '/class-wp-editor.php' );
+	}
+
+	_WP_Editors::enqueue_default_editor();
+}
+
+/**
  * Retrieves the contents of the search WordPress query variable.
  *
  * The search query string is passed through esc_attr() to ensure that it is safe
diff --git a/src/wp-includes/js/mce-view.js b/src/wp-includes/js/mce-view.js
index 21b2c87..397af63 100644
--- a/src/wp-includes/js/mce-view.js
+++ b/src/wp-includes/js/mce-view.js
@@ -729,6 +729,9 @@
 					$( node ).data( 'rendered', false );
 					editor.dom.setAttrib( node, 'data-wpview-text', encodeURIComponent( text ) );
 					wp.mce.views.createInstance( type, text, match.options, force ).render();
+
+					editor.selection.select( node );
+					editor.nodeChanged();
 					editor.focus();
 
 					return true;
diff --git a/src/wp-includes/js/swfupload/handlers.js b/src/wp-includes/js/swfupload/handlers.js
index e27bd37..8251f05 100644
--- a/src/wp-includes/js/swfupload/handlers.js
+++ b/src/wp-includes/js/swfupload/handlers.js
@@ -356,7 +356,7 @@ function cancelUpload() {
 
 // remember the last used image size, alignment and url
 jQuery(document).ready(function($){
-	$('input[type="radio"]', '#media-items').live('click', function(){
+	$( 'input[type="radio"]', '#media-items' ).on( 'click', function(){
 		var tr = $(this).closest('tr');
 
 		if ( $(tr).hasClass('align') )
@@ -365,7 +365,7 @@ jQuery(document).ready(function($){
 			setUserSetting('imgsize', $(this).val());
 	});
 
-	$('button.button', '#media-items').live('click', function(){
+	$( 'button.button', '#media-items' ).on( 'click', function(){
 		var c = this.className || '';
 		c = c.match(/url([^ '"]+)/);
 		if ( c && c[1] ) {
diff --git a/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js b/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js
index 20cdcd4..08e3cf7 100644
--- a/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js
+++ b/src/wp-includes/js/tinymce/plugins/wordpress/plugin.js
@@ -992,7 +992,7 @@ tinymce.PluginManager.add( 'wordpress', function( editor ) {
 
 		function hide( event ) {
 			if ( activeToolbar ) {
-				if ( activeToolbar.tempHide || event.type === 'hide' ) {
+				if ( activeToolbar.tempHide || event.type === 'hide' || event.type === 'blur' ) {
 					activeToolbar.hide();
 					activeToolbar = false;
 				} else if ( (
diff --git a/src/wp-includes/js/wp-a11y.js b/src/wp-includes/js/wp-a11y.js
index 2507cbc..7ccdd75 100644
--- a/src/wp-includes/js/wp-a11y.js
+++ b/src/wp-includes/js/wp-a11y.js
@@ -4,7 +4,8 @@ window.wp = window.wp || {};
 	'use strict';
 
 	var $containerPolite,
-		$containerAssertive;
+		$containerAssertive,
+		previousMessage = '';
 
 	/**
 	 * Update the ARIA live notification area text node.
@@ -23,6 +24,17 @@ window.wp = window.wp || {};
 		// Ensure only text is sent to screen readers.
 		message = $( '<p>' ).html( message ).text();
 
+		/*
+		 * Safari 10+VoiceOver don't announce repeated, identical strings. We use
+		 * a `no-break space` to force them to think identical strings are different.
+		 * See ticket #36853.
+		 */
+		if ( previousMessage === message ) {
+			message = message + '\u00A0';
+		}
+
+		previousMessage = message;
+
 		if ( $containerAssertive && 'assertive' === ariaLive ) {
 			$containerAssertive.text( message );
 		} else if ( $containerPolite ) {
diff --git a/src/wp-includes/media.php b/src/wp-includes/media.php
index 195d0f9..6253bd5 100644
--- a/src/wp-includes/media.php
+++ b/src/wp-includes/media.php
@@ -3324,20 +3324,23 @@ function wp_enqueue_media( $args = array() ) {
 	/**
 	 * Allows showing or hiding the "Create Audio Playlist" button in the media library.
 	 *
-	 * By default (if this filter returns `null`), a query will be run to
-	 * determine whether the media library contains any audio items.  This
-	 * query is expensive for large media libraries, so it may be desirable for
-	 * sites to override this behavior.
+	 * By default, the "Create Audio Playlist" button will always be shown in
+	 * the media library.  If this filter returns `null`, a query will be run
+	 * to determine whether the media library contains any audio items.  This
+	 * was the default behavior prior to version 4.8.0, but this query is
+	 * expensive for large media libraries.
 	 *
 	 * @since 4.7.4
+	 * @since 4.8.0 The filter's default value is `true` rather than `null`.
 	 *
 	 * @link https://core.trac.wordpress.org/ticket/31071
 	 *
-	 * @return bool|null Whether to show the button, or `null` for default behavior.
+	 * @param bool|null Whether to show the button, or `null` to decide based
+	 *                  on whether any audio files exist in the media library.
 	 */
-	$has_audio = apply_filters( 'media_has_audio', null );
-	if ( is_null( $has_audio ) ) {
-		$has_audio = $wpdb->get_var( "
+	$show_audio_playlist = apply_filters( 'media_library_show_audio_playlist', true );
+	if ( null === $show_audio_playlist ) {
+		$show_audio_playlist = $wpdb->get_var( "
 			SELECT ID
 			FROM $wpdb->posts
 			WHERE post_type = 'attachment'
@@ -3349,20 +3352,23 @@ function wp_enqueue_media( $args = array() ) {
 	/**
 	 * Allows showing or hiding the "Create Video Playlist" button in the media library.
 	 *
-	 * By default (if this filter returns `null`), a query will be run to
-	 * determine whether the media library contains any video items.  This
-	 * query is expensive for large media libraries, so it may be desirable for
-	 * sites to override this behavior.
+	 * By default, the "Create Video Playlist" button will always be shown in
+	 * the media library.  If this filter returns `null`, a query will be run
+	 * to determine whether the media library contains any video items.  This
+	 * was the default behavior prior to version 4.8.0, but this query is
+	 * expensive for large media libraries.
 	 *
 	 * @since 4.7.4
+	 * @since 4.8.0 The filter's default value is `true` rather than `null`.
 	 *
 	 * @link https://core.trac.wordpress.org/ticket/31071
 	 *
-	 * @return bool|null Whether to show the button, or `null` for default behavior.
+	 * @param bool|null Whether to show the button, or `null` to decide based
+	 *                  on whether any video files exist in the media library.
 	 */
-	$has_video = apply_filters( 'media_has_video', null );
-	if ( is_null( $has_video ) ) {
-		$has_video = $wpdb->get_var( "
+	$show_video_playlist = apply_filters( 'media_library_show_video_playlist', true );
+	if ( null === $show_video_playlist ) {
+		$show_video_playlist = $wpdb->get_var( "
 			SELECT ID
 			FROM $wpdb->posts
 			WHERE post_type = 'attachment'
@@ -3383,11 +3389,11 @@ function wp_enqueue_media( $args = array() ) {
 	 *
 	 * @link https://core.trac.wordpress.org/ticket/31071
 	 *
-	 * @return array|null An array of objects with `month` and `year`
-	 *                    properties, or `null` (or any other non-array value)
-	 *                    for default behavior.
+	 * @param array|null An array of objects with `month` and `year`
+	 *                   properties, or `null` (or any other non-array value)
+	 *                   for default behavior.
 	 */
-	$months = apply_filters( 'media_months', null );
+	$months = apply_filters( 'media_library_months_with_files', null );
 	if ( ! is_array( $months ) ) {
 		$months = $wpdb->get_results( $wpdb->prepare( "
 			SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
@@ -3414,14 +3420,14 @@ function wp_enqueue_media( $args = array() ) {
 		),
 		'defaultProps' => $props,
 		'attachmentCounts' => array(
-			'audio' => ( $has_audio ) ? 1 : 0,
-			'video' => ( $has_video ) ? 1 : 0
+			'audio' => ( $show_audio_playlist ) ? 1 : 0,
+			'video' => ( $show_video_playlist ) ? 1 : 0,
 		),
 		'embedExts'    => $exts,
 		'embedMimes'   => $ext_mimes,
 		'contentWidth' => $content_width,
 		'months'       => $months,
-		'mediaTrash'   => MEDIA_TRASH ? 1 : 0
+		'mediaTrash'   => MEDIA_TRASH ? 1 : 0,
 	);
 
 	$post = null;
diff --git a/src/wp-includes/ms-functions.php b/src/wp-includes/ms-functions.php
index 9df86cf..d209435 100644
--- a/src/wp-includes/ms-functions.php
+++ b/src/wp-includes/ms-functions.php
@@ -2274,10 +2274,13 @@ function wp_schedule_update_network_counts() {
  * Update the network-wide counts for the current network.
  *
  * @since 3.1.0
+ * @since 4.8.0 The $network_id parameter has been added.
+ *
+ * @param int|null $network_id ID of the network. Default is the current network.
  */
-function wp_update_network_counts() {
-	wp_update_network_user_counts();
-	wp_update_network_site_counts();
+function wp_update_network_counts( $network_id = null ) {
+	wp_update_network_user_counts( $network_id );
+	wp_update_network_site_counts( $network_id );
 }
 
 /**
@@ -2329,35 +2332,42 @@ function wp_maybe_update_network_user_counts() {
  * Update the network-wide site count.
  *
  * @since 3.7.0
+ * @since 4.8.0 The $network_id parameter has been added.
  *
- * @global wpdb $wpdb WordPress database abstraction object.
+ * @param int|null $network_id ID of the network. Default is the current network.
  */
-function wp_update_network_site_counts() {
-	global $wpdb;
+function wp_update_network_site_counts( $network_id = null ) {
+	$network_id = (int) $network_id;
+	if ( ! $network_id ) {
+		$network_id = get_current_network_id();
+	}
 
 	$count = get_sites( array(
-		'network_id' => $wpdb->siteid,
+		'network_id' => $network_id,
 		'spam'       => 0,
 		'deleted'    => 0,
 		'archived'   => 0,
 		'count'      => true,
 	) );
 
-	update_site_option( 'blog_count', $count );
+	update_network_option( $network_id, 'blog_count', $count );
 }
 
 /**
  * Update the network-wide user count.
  *
  * @since 3.7.0
+ * @since 4.8.0 The $network_id parameter has been added.
  *
  * @global wpdb $wpdb WordPress database abstraction object.
+ *
+ * @param int|null $network_id ID of the network. Default is the current network.
  */
-function wp_update_network_user_counts() {
+function wp_update_network_user_counts( $network_id = null ) {
 	global $wpdb;
 
 	$count = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
-	update_site_option( 'user_count', $count );
+	update_network_option( $network_id, 'user_count', $count );
 }
 
 /**
diff --git a/src/wp-includes/option.php b/src/wp-includes/option.php
index d58ae69..8bc8edc 100644
--- a/src/wp-includes/option.php
+++ b/src/wp-includes/option.php
@@ -1641,7 +1641,7 @@ function get_site_transient( $transient ) {
  * @see set_transient()
  *
  * @param string $transient  Transient name. Expected to not be SQL-escaped. Must be
- *                           40 characters or fewer in length.
+ *                           167 characters or fewer in length.
  * @param mixed  $value      Transient value. Expected to not be SQL-escaped.
  * @param int    $expiration Optional. Time until expiration in seconds. Default 0 (no expiration).
  * @return bool False if value was not set and true if value was set.
diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php
index a33236c..0d62400 100644
--- a/src/wp-includes/post.php
+++ b/src/wp-includes/post.php
@@ -815,7 +815,7 @@ function register_post_status( $post_status, $args = array() ) {
 		$args->label = $post_status;
 
 	if ( false === $args->label_count )
-		$args->label_count = array( $args->label, $args->label );
+		$args->label_count = _n_noop( $args->label, $args->label );
 
 	$wp_post_statuses[$post_status] = $args;
 
@@ -2800,10 +2800,12 @@ function wp_untrash_post_comments( $post = null ) {
  *
  * @param int   $post_id Optional. The Post ID. Does not default to the ID of the
  *                       global $post. Default 0.
- * @param array $args    Optional. Category arguments. See wp_get_object_terms(). Default empty.
- * @return array List of categories. If the `$fields` argument passed via `$args` is 'all' or
- *               'all_with_object_id', an array of WP_Term objects will be returned. If `$fields`
- *               is 'ids', an array of category ids. If `$fields` is 'names', an array of category names.
+ * @param array $args    Optional. Category query parameters. Default empty array.
+ *                       See WP_Term_Query::__construct() for supported arguments.
+ * @return array|WP_Error List of categories. If the `$fields` argument passed via `$args` is 'all' or
+ *                        'all_with_object_id', an array of WP_Term objects will be returned. If `$fields`
+ *                        is 'ids', an array of category ids. If `$fields` is 'names', an array of category names.
+ *                        WP_Error object if 'category' taxonomy doesn't exist.
  */
 function wp_get_post_categories( $post_id = 0, $args = array() ) {
 	$post_id = (int) $post_id;
@@ -2826,8 +2828,10 @@ function wp_get_post_categories( $post_id = 0, $args = array() ) {
  *
  * @param int   $post_id Optional. The Post ID. Does not default to the ID of the
  *                       global $post. Default 0.
- * @param array $args Optional. Overwrite the defaults
- * @return array List of post tags.
+ * @param array $args    Optional. Tag query parameters. Default empty array.
+ *                       See WP_Term_Query::__construct() for supported arguments.
+ * @return array|WP_Error Array of WP_Term objects on success or empty array if no tags were found.
+ *                        WP_Error object if 'post_tag' taxonomy doesn't exist.
  */
 function wp_get_post_tags( $post_id = 0, $args = array() ) {
 	return wp_get_post_terms( $post_id, 'post_tag', $args);
@@ -2845,9 +2849,10 @@ function wp_get_post_tags( $post_id = 0, $args = array() ) {
  * @param int    $post_id  Optional. The Post ID. Does not default to the ID of the
  *                         global $post. Default 0.
  * @param string $taxonomy Optional. The taxonomy for which to retrieve terms. Default 'post_tag'.
- * @param array  $args     Optional. wp_get_object_terms() arguments. Default empty array.
- * @return array|WP_Error  List of post terms or empty array if no terms were found. WP_Error object
- *                         if `$taxonomy` doesn't exist.
+ * @param array  $args     Optional. Term query parameters. Default empty array.
+ *                         See WP_Term_Query::__construct() for supported arguments.
+ * @return array|WP_Error  Array of WP_Term objects on success or empty array if no terms were found.
+ *                         WP_Error object if `$taxonomy` doesn't exist.
  */
 function wp_get_post_terms( $post_id = 0, $taxonomy = 'post_tag', $args = array() ) {
 	$post_id = (int) $post_id;
diff --git a/src/wp-includes/taxonomy.php b/src/wp-includes/taxonomy.php
index 94f3668..f1c1bce 100644
--- a/src/wp-includes/taxonomy.php
+++ b/src/wp-includes/taxonomy.php
@@ -1841,10 +1841,30 @@ function wp_get_object_terms($object_ids, $taxonomies, $args = array()) {
 
 	$args = wp_parse_args( $args );
 
+	/*
+	 * When one or more queried taxonomies is registered with an 'args' array,
+	 * those params override the `$args` passed to this function.
+	 */
+	$terms = array();
+	if ( count( $taxonomies ) > 1 ) {
+		foreach ( $taxonomies as $index => $taxonomy ) {
+			$t = get_taxonomy( $taxonomy );
+			if ( isset( $t->args ) && is_array( $t->args ) && $args != array_merge( $args, $t->args ) ) {
+				unset( $taxonomies[ $index ] );
+				$terms = array_merge( $terms, wp_get_object_terms( $object_ids, $taxonomy, array_merge( $args, $t->args ) ) );
+			}
+		}
+	} else {
+		$t = get_taxonomy( $taxonomies[0] );
+		if ( isset( $t->args ) && is_array( $t->args ) ) {
+			$args = array_merge( $args, $t->args );
+		}
+	}
+
 	$args['taxonomy'] = $taxonomies;
 	$args['object_ids'] = $object_ids;
 
-	$terms = get_terms( $args );
+	$terms = array_merge( $terms, get_terms( $args ) );
 
 	/**
 	 * Filters the terms for a given object or objects.
diff --git a/src/wp-includes/user.php b/src/wp-includes/user.php
index dcd1026..2bb4e05 100644
--- a/src/wp-includes/user.php
+++ b/src/wp-includes/user.php
@@ -857,7 +857,12 @@ function count_users($strategy = 'time') {
 		$select_count = implode(', ', $select_count);
 
 		// Add the meta_value index to the selection list, then run the query.
-		$row = $wpdb->get_row( "SELECT $select_count, COUNT(*) FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'", ARRAY_N );
+		$row = $wpdb->get_row( "
+			SELECT {$select_count}, COUNT(*)
+			FROM {$wpdb->usermeta}
+			INNER JOIN {$wpdb->users} ON user_id = ID
+			WHERE meta_key = '{$blog_prefix}capabilities'
+		", ARRAY_N );
 
 		// Run the previous loop again to associate results with role names.
 		$col = 0;
@@ -881,7 +886,12 @@ function count_users($strategy = 'time') {
 			'none' => 0,
 		);
 
-		$users_of_blog = $wpdb->get_col( "SELECT meta_value FROM $wpdb->usermeta WHERE meta_key = '{$blog_prefix}capabilities'" );
+		$users_of_blog = $wpdb->get_col( "
+			SELECT meta_value
+			FROM {$wpdb->usermeta}
+			INNER JOIN {$wpdb->users} ON user_id = ID
+			WHERE meta_key = '{$blog_prefix}capabilities'
+		" );
 
 		foreach ( $users_of_blog as $caps_meta ) {
 			$b_roles = maybe_unserialize($caps_meta);
diff --git a/tests/phpunit/includes/bootstrap.php b/tests/phpunit/includes/bootstrap.php
index b462e70..1450736 100644
--- a/tests/phpunit/includes/bootstrap.php
+++ b/tests/phpunit/includes/bootstrap.php
@@ -3,6 +3,12 @@
  * Installs WordPress for running the tests and loads WordPress and the test libraries
  */
 
+/**
+ * Compatibility with PHPUnit 6+
+ */
+if ( class_exists( 'PHPUnit\Runner\Version' ) ) {
+	require_once dirname( __FILE__ ) . '/phpunit6-compat.php';
+}
 
 $config_file_path = dirname( dirname( __FILE__ ) );
 if ( ! file_exists( $config_file_path . '/wp-tests-config.php' ) ) {
diff --git a/tests/phpunit/includes/object-cache.php b/tests/phpunit/includes/object-cache.php
new file mode 100644
index 0000000..51f3e91
--- /dev/null
+++ b/tests/phpunit/includes/object-cache.php
@@ -0,0 +1,2106 @@
+<?php
+/**
+ * Adds a value to cache.
+ *
+ * If the specified key already exists, the value is not stored and the function
+ * returns false.
+ *
+ * @link http://www.php.net/manual/en/memcached.add.php
+ *
+ * @param string    $key        The key under which to store the value.
+ * @param mixed     $value      The value to store.
+ * @param string    $group      The group value appended to the $key.
+ * @param int       $expiration The expiration time, defaults to 0.
+ * @return bool                 Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_add( $key, $value, $group = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->add( $key, $value, $group, $expiration );
+}
+
+/**
+ * Adds a value to cache on a specific server.
+ *
+ * Using a server_key value, the object can be stored on a specified server as opposed
+ * to a random server in the stack. Note that this method will add the key/value to the
+ * _cache object as part of the runtime cache. It will add it to an array for the
+ * specified server_key.
+ *
+ * @link http://www.php.net/manual/en/memcached.addbykey.php
+ *
+ * @param string    $server_key     The key identifying the server to store the value on.
+ * @param string    $key            The key under which to store the value.
+ * @param mixed     $value          The value to store.
+ * @param string    $group          The group value appended to the $key.
+ * @param int       $expiration     The expiration time, defaults to 0.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_add_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->addByKey( $server_key, $key, $value, $group, $expiration );
+}
+
+/**
+ * Add a single server to the list of Memcached servers.
+ *
+ * @link http://www.php.net/manual/en/memcached.addserver.php
+ *
+ * @param string        $host   The hostname of the memcache server.
+ * @param int           $port   The port on which memcache is running.
+ * @param int           $weight The weight of the server relative to the total weight of all the servers in the pool.
+ * @return bool                 Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_add_server( $host, $port, $weight = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->addServer( $host, $port, $weight );
+}
+
+/**
+ * Adds an array of servers to the pool.
+ *
+ * Each individual server in the array must include a domain and port, with an optional
+ * weight value: $servers = array( array( '127.0.0.1', 11211, 0 ) );
+ *
+ * @link http://www.php.net/manual/en/memcached.addservers.php
+ *
+ * @param array     $servers    Array of server to register.
+ * @return bool                 True on success; false on failure.
+ */
+function wp_cache_add_servers( $servers ) {
+	global $wp_object_cache;
+	return $wp_object_cache->addServers( $servers );
+}
+
+/**
+ * Append data to an existing item.
+ *
+ * This method should throw an error if it is used with compressed data. This
+ * is an expected behavior. Memcached casts the value to be appended to the initial value to the
+ * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to
+ * how memcached treats types, the behavior has been mimicked in the internal cache to produce
+ * similar results and improve consistency. It is recommend that appends only occur with data of
+ * the same type.
+ *
+ * @link http://www.php.net/manual/en/memcached.append.php
+ *
+ * @param string    $key    The key under which to store the value.
+ * @param mixed     $value  Must be string as appending mixed values is not well-defined
+ * @param string    $group  The group value appended to the $key.
+ * @return bool             Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_append( $key, $value, $group = '' ) {
+	global $wp_object_cache;
+	return $wp_object_cache->append( $key, $value, $group );
+}
+
+/**
+ * Append data to an existing item by server key.
+ *
+ * This method should throw an error if it is used with compressed data. This
+ * is an expected behavior. Memcached casts the value to be appended to the initial value to the
+ * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to
+ * how memcached treats types, the behavior has been mimicked in the internal cache to produce
+ * similar results and improve consistency. It is recommend that appends only occur with data of
+ * the same type.
+ *
+ * @link http://www.php.net/manual/en/memcached.appendbykey.php
+ *
+ * @param string    $server_key     The key identifying the server to store the value on.
+ * @param string    $key            The key under which to store the value.
+ * @param mixed     $value          Must be string as appending mixed values is not well-defined
+ * @param string    $group          The group value appended to the $key.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_append_by_key( $server_key, $key, $value, $group = '' ) {
+	global $wp_object_cache;
+	return $wp_object_cache->appendByKey( $server_key, $key, $value, $group );
+}
+
+/**
+ * Performs a "check and set" to store data.
+ *
+ * The set will be successful only if the no other request has updated the value since it was fetched by
+ * this request.
+ *
+ * @link http://www.php.net/manual/en/memcached.cas.php
+ *
+ * @param float     $cas_token  Unique value associated with the existing item. Generated by memcached.
+ * @param string    $key        The key under which to store the value.
+ * @param mixed     $value      The value to store.
+ * @param string    $group      The group value appended to the $key.
+ * @param int       $expiration The expiration time, defaults to 0.
+ * @return bool                 Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_cas( $cas_token, $key, $value, $group = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->cas( $cas_token, $key, $value, $group, $expiration );
+}
+
+/**
+ * Performs a "check and set" to store data with a server key.
+ *
+ * The set will be successful only if the no other request has updated the value since it was fetched by
+ * this request.
+ *
+ * @link http://www.php.net/manual/en/memcached.casbykey.php
+ *
+ * @param string    $server_key The key identifying the server to store the value on.
+ * @param float     $cas_token  Unique value associated with the existing item. Generated by memcached.
+ * @param string    $key        The key under which to store the value.
+ * @param mixed     $value      The value to store.
+ * @param string    $group      The group value appended to the $key.
+ * @param int       $expiration The expiration time, defaults to 0.
+ * @return bool                 Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_cas_by_key( $cas_token, $server_key, $key, $value, $group = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->casByKey( $cas_token, $server_key, $key, $value, $group, $expiration );
+}
+
+/**
+ * Closes the cache.
+ *
+ * This function has ceased to do anything since WordPress 2.5. The
+ * functionality was removed along with the rest of the persistent cache. This
+ * does not mean that plugins can't implement this function when they need to
+ * make sure that the cache is cleaned up after WordPress no longer needs it.
+ *
+ * @since 2.0.0
+ *
+ * @return  bool    Always returns True
+ */
+function wp_cache_close() {
+	return true;
+}
+
+/**
+ * Decrement a numeric item's value.
+ *
+ * @link http://www.php.net/manual/en/memcached.decrement.php
+ *
+ * @param string    $key    The key under which to store the value.
+ * @param int       $offset The amount by which to decrement the item's value.
+ * @param string    $group  The group value appended to the $key.
+ * @return int|bool         Returns item's new value on success or FALSE on failure.
+ */
+function wp_cache_decrement( $key, $offset = 1, $group = '' ) {
+	global $wp_object_cache;
+	return $wp_object_cache->decrement( $key, $offset, $group );
+}
+
+/**
+ * Decrement a numeric item's value.
+ *
+ * Same as wp_cache_decrement. Original WordPress caching backends use wp_cache_decr. I
+ * want both spellings to work.
+ *
+ * @link http://www.php.net/manual/en/memcached.decrement.php
+ *
+ * @param string    $key    The key under which to store the value.
+ * @param int       $offset The amount by which to decrement the item's value.
+ * @param string    $group  The group value appended to the $key.
+ * @return int|bool         Returns item's new value on success or FALSE on failure.
+ */
+function wp_cache_decr( $key, $offset = 1, $group = '' ) {
+	return wp_cache_decrement( $key, $offset, $group );
+}
+
+/**
+ * Remove the item from the cache.
+ *
+ * Remove an item from memcached with identified by $key after $time seconds. The
+ * $time parameter allows an object to be queued for deletion without immediately
+ * deleting. Between the time that it is queued and the time it's deleted, add,
+ * replace, and get will fail, but set will succeed.
+ *
+ * @link http://www.php.net/manual/en/memcached.delete.php
+ *
+ * @param string    $key    The key under which to store the value.
+ * @param string    $group  The group value appended to the $key.
+ * @param int       $time   The amount of time the server will wait to delete the item in seconds.
+ * @return bool             Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_delete( $key, $group = '', $time = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->delete( $key, $group, $time );
+}
+
+/**
+ * Remove the item from the cache by server key.
+ *
+ * Remove an item from memcached with identified by $key after $time seconds. The
+ * $time parameter allows an object to be queued for deletion without immediately
+ * deleting. Between the time that it is queued and the time it's deleted, add,
+ * replace, and get will fail, but set will succeed.
+ *
+ * @link http://www.php.net/manual/en/memcached.deletebykey.php
+ *
+ * @param string        $server_key The key identifying the server to store the value on.
+ * @param string        $key        The key under which to store the value.
+ * @param string        $group      The group value appended to the $key.
+ * @param int           $time       The amount of time the server will wait to delete the item in seconds.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_delete_by_key( $server_key, $key, $group = '', $time = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->deleteByKey( $server_key, $key, $group, $time );
+}
+
+/**
+ * Fetch the next result.
+ *
+ * @link http://www.php.net/manual/en/memcached.fetch.php
+ *
+ * @return  array|bool   Returns the next result or FALSE otherwise.
+ */
+function wp_cache_fetch() {
+	global $wp_object_cache;
+	return $wp_object_cache->fetch();
+}
+
+/**
+ * Fetch all remaining results from the last request.
+ *
+ * @link http://www.php.net/manual/en/memcached.fetchall.php
+ *
+ * @return  array|bool  Returns the results or FALSE on failure.
+ */
+function wp_cache_fetch_all() {
+	global $wp_object_cache;
+	return $wp_object_cache->fetchAll();
+}
+
+/**
+ * Invalidate all items in the cache.
+ *
+ * @link http://www.php.net/manual/en/memcached.flush.php
+ *
+ * @param int       $delay  Number of seconds to wait before invalidating the items.
+ * @return bool             Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_flush( $delay = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->flush( $delay );
+}
+
+/**
+ * Retrieve object from cache.
+ *
+ * Gets an object from cache based on $key and $group. In order to fully support the $cache_cb and $cas_token
+ * parameters, the runtime cache is ignored by this function if either of those values are set. If either of
+ * those values are set, the request is made directly to the memcached server for proper handling of the
+ * callback and/or token.
+ *
+ * Note that the $deprecated and $found args are only here for compatibility with the native wp_cache_get function.
+ *
+ * @link http://www.php.net/manual/en/memcached.get.php
+ *
+ * @param string        $key        The key under which to store the value.
+ * @param string        $group      The group value appended to the $key.
+ * @param bool          $force      Whether or not to force a cache invalidation.
+ * @param null|bool     $found      Variable passed by reference to determine if the value was found or not.
+ * @param null|string   $cache_cb   Read-through caching callback.
+ * @param null|float    $cas_token  The variable to store the CAS token in.
+ * @return bool|mixed               Cached object value.
+ */
+function wp_cache_get( $key, $group = '', $force = false, &$found = null, $cache_cb = null, &$cas_token = null ) {
+	global $wp_object_cache;
+
+	if ( func_num_args() > 4 )
+		return $wp_object_cache->get( $key, $group, $force, $found, '', false, $cache_cb, $cas_token );
+	else
+		return $wp_object_cache->get( $key, $group, $force, $found );
+}
+
+/**
+ * Retrieve object from cache from specified server.
+ *
+ * Gets an object from cache based on $key, $group and $server_key. In order to fully support the $cache_cb and $cas_token
+ * parameters, the runtime cache is ignored by this function if either of those values are set. If either of
+ * those values are set, the request is made directly to the memcached server for proper handling of the
+ * callback and/or token.
+ *
+ * @link http://www.php.net/manual/en/memcached.getbykey.php
+ *
+ * @param string        $server_key The key identifying the server to store the value on.
+ * @param string        $key        The key under which to store the value.
+ * @param string        $group      The group value appended to the $key.
+ * @param bool          $force      Whether or not to force a cache invalidation.
+ * @param null|bool     $found      Variable passed by reference to determine if the value was found or not.
+ * @param null|string   $cache_cb   Read-through caching callback.
+ * @param null|float    $cas_token  The variable to store the CAS token in.
+ * @return bool|mixed               Cached object value.
+ */
+function wp_cache_get_by_key( $server_key, $key, $group = '', $force = false, &$found = null, $cache_cb = NULL, &$cas_token = NULL ) {
+	global $wp_object_cache;
+
+	if ( func_num_args() > 5 )
+		return $wp_object_cache->getByKey( $server_key, $key, $group, $force, $found, $cache_cb, $cas_token );
+	else
+		return $wp_object_cache->getByKey( $server_key, $key, $group, $force, $found );
+}
+
+/**
+ * Request multiple keys without blocking.
+ *
+ * @link http://www.php.net/manual/en/memcached.getdelayed.php
+ *
+ * @param string|array  $keys       Array or string of key(s) to request.
+ * @param string|array  $groups     Array or string of group(s) for the key(s). See buildKeys for more on how these are handled.
+ * @param bool          $with_cas   Whether to request CAS token values also.
+ * @param null          $value_cb   The result callback or NULL.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_get_delayed( $keys, $groups = '', $with_cas = false, $value_cb = NULL ) {
+	global $wp_object_cache;
+	return $wp_object_cache->getDelayed( $keys, $groups, $with_cas, $value_cb );
+}
+
+/**
+ * Request multiple keys without blocking from a specified server.
+ *
+ * @link http://www.php.net/manual/en/memcached.getdelayed.php
+ *
+ * @param string        $server_key The key identifying the server to store the value on.
+ * @param string|array  $keys       Array or string of key(s) to request.
+ * @param string|array  $groups     Array or string of group(s) for the key(s). See buildKeys for more on how these are handled.
+ * @param bool          $with_cas   Whether to request CAS token values also.
+ * @param null          $value_cb   The result callback or NULL.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_get_delayed_by_key( $server_key, $keys, $groups = '', $with_cas = false, $value_cb = NULL ) {
+	global $wp_object_cache;
+	return $wp_object_cache->getDelayedByKey( $server_key, $keys, $groups, $with_cas, $value_cb );
+}
+
+/**
+ * Gets multiple values from memcached in one request.
+ *
+ * See the buildKeys method definition to understand the $keys/$groups parameters.
+ *
+ * @link http://www.php.net/manual/en/memcached.getmulti.php
+ *
+ * @param array         $keys       Array of keys to retrieve.
+ * @param string|array  $groups     If string, used for all keys. If arrays, corresponds with the $keys array.
+ * @param null|array    $cas_tokens The variable to store the CAS tokens for the found items.
+ * @param int           $flags      The flags for the get operation.
+ * @return bool|array               Returns the array of found items or FALSE on failure.
+ */
+function wp_cache_get_multi( $keys, $groups = '', &$cas_tokens = NULL, $flags = NULL ) {
+	global $wp_object_cache;
+
+	if ( func_num_args() > 2 )
+		return $wp_object_cache->getMulti( $keys, $groups, '', $cas_tokens, $flags );
+	else
+		return $wp_object_cache->getMulti( $keys, $groups );
+}
+
+/**
+ * Gets multiple values from memcached in one request by specified server key.
+ *
+ * See the buildKeys method definition to understand the $keys/$groups parameters.
+ *
+ * @link http://www.php.net/manual/en/memcached.getmultibykey.php
+ *
+ * @param string        $server_key The key identifying the server to store the value on.
+ * @param array         $keys       Array of keys to retrieve.
+ * @param string|array  $groups     If string, used for all keys. If arrays, corresponds with the $keys array.
+ * @param null|array    $cas_tokens The variable to store the CAS tokens for the found items.
+ * @param int           $flags      The flags for the get operation.
+ * @return bool|array               Returns the array of found items or FALSE on failure.
+ */
+function wp_cache_get_multi_by_key( $server_key, $keys, $groups = '', &$cas_tokens = NULL, $flags = NULL ) {
+	global $wp_object_cache;
+
+	if ( func_num_args() > 3 )
+		return $wp_object_cache->getMultiByKey( $server_key, $keys, $groups, $cas_tokens, $flags );
+	else
+		return $wp_object_cache->getMultiByKey( $server_key, $keys, $groups );
+}
+
+/**
+ * Retrieve a Memcached option value.
+ *
+ * @link http://www.php.net/manual/en/memcached.getoption.php
+ *
+ * @param int   $option One of the Memcached::OPT_* constants.
+ * @return mixed        Returns the value of the requested option, or FALSE on error.
+ */
+function wp_cache_get_option( $option ) {
+	global $wp_object_cache;
+	return $wp_object_cache->getOption( $option );
+}
+
+/**
+ * Return the result code of the last option.
+ *
+ * @link http://www.php.net/manual/en/memcached.getresultcode.php
+ *
+ * @return int  Result code of the last Memcached operation.
+ */
+function wp_cache_get_result_code() {
+	global $wp_object_cache;
+	return $wp_object_cache->getResultCode();
+}
+
+/**
+ * Return the message describing the result of the last operation.
+ *
+ * @link http://www.php.net/manual/en/memcached.getresultmessage.php
+ *
+ * @return string   Message describing the result of the last Memcached operation.
+ */
+function wp_cache_get_result_message() {
+	global $wp_object_cache;
+	return $wp_object_cache->getResultMessage();
+}
+
+/**
+ * Get server information by key.
+ *
+ * @link http://www.php.net/manual/en/memcached.getserverbykey.php
+ *
+ * @param string    $server_key The key identifying the server to store the value on.
+ * @return array                Array with host, post, and weight on success, FALSE on failure.
+ */
+function wp_cache_get_server_by_key( $server_key ) {
+	global $wp_object_cache;
+	return $wp_object_cache->getServerByKey( $server_key );
+}
+
+/**
+ * Get the list of servers in the pool.
+ *
+ * @link http://www.php.net/manual/en/memcached.getserverlist.php
+ *
+ * @return array    The list of all servers in the server pool.
+ */
+function wp_cache_get_server_list() {
+	global $wp_object_cache;
+	return $wp_object_cache->getServerList();
+}
+
+/**
+ * Get server pool statistics.
+ *
+ * @link http://www.php.net/manual/en/memcached.getstats.php
+ *
+ * @return array    Array of server statistics, one entry per server.
+ */
+function wp_cache_get_stats() {
+	global $wp_object_cache;
+	return $wp_object_cache->getStats();
+}
+
+/**
+ * Get server pool memcached version information.
+ *
+ * @link http://www.php.net/manual/en/memcached.getversion.php
+ *
+ * @return array    Array of server versions, one entry per server.
+ */
+function wp_cache_get_version() {
+	global $wp_object_cache;
+	return $wp_object_cache->getVersion();
+}
+
+/**
+ * Increment a numeric item's value.
+ *
+ * @link http://www.php.net/manual/en/memcached.increment.php
+ *
+ * @param string    $key    The key under which to store the value.
+ * @param int       $offset The amount by which to increment the item's value.
+ * @param string    $group  The group value appended to the $key.
+ * @return int|bool         Returns item's new value on success or FALSE on failure.
+ */
+function wp_cache_increment( $key, $offset = 1, $group = '' ) {
+	global $wp_object_cache;
+	return $wp_object_cache->increment( $key, $offset, $group );
+}
+
+/**
+ * Increment a numeric item's value.
+ *
+ * This is the same as wp_cache_increment, but kept for back compatibility. The original
+ * WordPress caching backends use wp_cache_incr. I want both to work.
+ *
+ * @link http://www.php.net/manual/en/memcached.increment.php
+ *
+ * @param string    $key    The key under which to store the value.
+ * @param int       $offset The amount by which to increment the item's value.
+ * @param string    $group  The group value appended to the $key.
+ * @return int|bool         Returns item's new value on success or FALSE on failure.
+ */
+function wp_cache_incr( $key, $offset = 1, $group = '' ) {
+	return wp_cache_increment( $key, $offset, $group );
+}
+
+/**
+ * Prepend data to an existing item.
+ *
+ * This method should throw an error if it is used with compressed data. This is an expected behavior.
+ * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be
+ * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to
+ * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer"
+ * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been
+ * mimicked in the internal cache to produce similar results and improve consistency. It is recommend
+ * that prepends only occur with data of the same type.
+ *
+ * @link http://www.php.net/manual/en/memcached.prepend.php
+ *
+ * @param string    $key    The key under which to store the value.
+ * @param string    $value  Must be string as prepending mixed values is not well-defined.
+ * @param string    $group  The group value prepended to the $key.
+ * @return bool             Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_prepend( $key, $value, $group = '' ) {
+	global $wp_object_cache;
+	return $wp_object_cache->prepend( $key, $value, $group );
+}
+
+/**
+ * Append data to an existing item by server key.
+ *
+ * This method should throw an error if it is used with compressed data. This is an expected behavior.
+ * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be
+ * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to
+ * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer"
+ * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been
+ * mimicked in the internal cache to produce similar results and improve consistency. It is recommend
+ * that prepends only occur with data of the same type.
+ *
+ * @link http://www.php.net/manual/en/memcached.prependbykey.php
+ *
+ * @param string    $server_key     The key identifying the server to store the value on.
+ * @param string    $key            The key under which to store the value.
+ * @param string    $value          Must be string as prepending mixed values is not well-defined.
+ * @param string    $group          The group value prepended to the $key.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_prepend_by_key( $server_key, $key, $value, $group = '' ) {
+	global $wp_object_cache;
+	return $wp_object_cache->prependByKey( $server_key, $key, $value, $group );
+}
+
+/**
+ * Replaces a value in cache.
+ *
+ * This method is similar to "add"; however, is does not successfully set a value if
+ * the object's key is not already set in cache.
+ *
+ * @link http://www.php.net/manual/en/memcached.replace.php
+ *
+ * @param string    $key        The key under which to store the value.
+ * @param mixed     $value      The value to store.
+ * @param string    $group      The group value appended to the $key.
+ * @param int       $expiration The expiration time, defaults to 0.
+ * @return bool                 Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_replace( $key, $value, $group = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->replace( $key, $value, $group, $expiration );
+}
+
+/**
+ * Replaces a value in cache on a specific server.
+ *
+ * This method is similar to "addByKey"; however, is does not successfully set a value if
+ * the object's key is not already set in cache.
+ *
+ * @link http://www.php.net/manual/en/memcached.addbykey.php
+ *
+ * @param string    $server_key     The key identifying the server to store the value on.
+ * @param string    $key            The key under which to store the value.
+ * @param mixed     $value          The value to store.
+ * @param string    $group          The group value appended to the $key.
+ * @param int       $expiration     The expiration time, defaults to 0.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_replace_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->replaceByKey( $server_key, $key, $value, $group, $expiration );
+}
+
+/**
+ * Sets a value in cache.
+ *
+ * The value is set whether or not this key already exists in memcached.
+ *
+ * @link http://www.php.net/manual/en/memcached.set.php
+ *
+ * @param string    $key        The key under which to store the value.
+ * @param mixed     $value      The value to store.
+ * @param string    $group      The group value appended to the $key.
+ * @param int       $expiration The expiration time, defaults to 0.
+ * @return bool                 Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_set( $key, $value, $group = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->set( $key, $value, $group, $expiration );
+}
+
+/**
+ * Sets a value in cache.
+ *
+ * The value is set whether or not this key already exists in memcached.
+ *
+ * @link http://www.php.net/manual/en/memcached.set.php
+ *
+ * @param string    $server_key     The key identifying the server to store the value on.
+ * @param string    $key            The key under which to store the value.
+ * @param mixed     $value          The value to store.
+ * @param string    $group          The group value appended to the $key.
+ * @param int       $expiration     The expiration time, defaults to 0.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_set_by_key( $server_key, $key, $value, $group = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->setByKey( $server_key, $key, $value, $group, $expiration );
+}
+
+/**
+ * Set multiple values to cache at once.
+ *
+ * By sending an array of $items to this function, all values are saved at once to
+ * memcached, reducing the need for multiple requests to memcached. The $items array
+ * keys and values are what are stored to memcached. The keys in the $items array
+ * are merged with the $groups array/string value via buildKeys to determine the
+ * final key for the object.
+ *
+ * @param array         $items      An array of key/value pairs to store on the server.
+ * @param string|array  $groups     Group(s) to merge with key(s) in $items.
+ * @param int           $expiration The expiration time, defaults to 0.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_set_multi( $items, $groups = '', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->setMulti( $items, $groups, $expiration );
+}
+
+/**
+ * Set multiple values to cache at once on specified server.
+ *
+ * By sending an array of $items to this function, all values are saved at once to
+ * memcached, reducing the need for multiple requests to memcached. The $items array
+ * keys and values are what are stored to memcached. The keys in the $items array
+ * are merged with the $groups array/string value via buildKeys to determine the
+ * final key for the object.
+ *
+ * @param string        $server_key The key identifying the server to store the value on.
+ * @param array         $items      An array of key/value pairs to store on the server.
+ * @param string|array  $groups     Group(s) to merge with key(s) in $items.
+ * @param int           $expiration The expiration time, defaults to 0.
+ * @return bool                     Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_set_multi_by_key( $server_key, $items, $groups = 'default', $expiration = 0 ) {
+	global $wp_object_cache;
+	return $wp_object_cache->setMultiByKey( $server_key, $items, $groups, $expiration );
+}
+
+/**
+ * Set a Memcached option.
+ *
+ * @link http://www.php.net/manual/en/memcached.setoption.php
+ *
+ * @param int       $option Option name.
+ * @param mixed     $value  Option value.
+ * @return bool             Returns TRUE on success or FALSE on failure.
+ */
+function wp_cache_set_option( $option, $value ) {
+	global $wp_object_cache;
+	return $wp_object_cache->setOption( $option, $value );
+}
+
+/**
+ * Switch blog prefix, which changes the cache that is accessed.
+ *
+ * @param  int     $blog_id    Blog to switch to.
+ * @return void
+ */
+function wp_cache_switch_to_blog( $blog_id ) {
+	global $wp_object_cache;
+	return $wp_object_cache->switch_to_blog( $blog_id );
+}
+
+
+/**
+ * Sets up Object Cache Global and assigns it.
+ *
+ * @global  WP_Object_Cache     $wp_object_cache    WordPress Object Cache
+ * @return  void
+ */
+function wp_cache_init() {
+	global $wp_object_cache;
+	$wp_object_cache = new WP_Object_Cache();
+}
+
+/**
+ * Adds a group or set of groups to the list of non-persistent groups.
+ *
+ * @param   string|array    $groups     A group or an array of groups to add.
+ * @return  void
+ */
+function wp_cache_add_global_groups( $groups ) {
+	global $wp_object_cache;
+	$wp_object_cache->add_global_groups( $groups );
+}
+
+/**
+ * Adds a group or set of groups to the list of non-Memcached groups.
+ *
+ * @param   string|array    $groups     A group or an array of groups to add.
+ * @return  void
+ */
+function wp_cache_add_non_persistent_groups( $groups ) {
+	global $wp_object_cache;
+	$wp_object_cache->add_non_persistent_groups( $groups );
+}
+
+class WP_Object_Cache {
+
+	/**
+	 * Holds the Memcached object.
+	 *
+	 * @var Memcached
+	 */
+	public $m;
+
+	/**
+	 * Hold the Memcached server details.
+	 *
+	 * @var array
+	 */
+	public $servers;
+
+	/**
+	 * Holds the non-Memcached objects.
+	 *
+	 * @var array
+	 */
+	public $cache = array();
+
+	/**
+	 * List of global groups.
+	 *
+	 * @var array
+	 */
+	public $global_groups = array( 'users', 'userlogins', 'usermeta', 'site-options', 'site-lookup', 'blog-lookup', 'blog-details', 'rss' );
+
+	/**
+	 * List of groups not saved to Memcached.
+	 *
+	 * @var array
+	 */
+	public $no_mc_groups = array( 'comment', 'counts' );
+
+	/**
+	 * Prefix used for global groups.
+	 *
+	 * @var string
+	 */
+	public $global_prefix = '';
+
+	/**
+	 * Prefix used for non-global groups.
+	 *
+	 * @var string
+	 */
+	public $blog_prefix = '';
+
+	/**
+	 * Instantiate the Memcached class.
+	 *
+	 * Instantiates the Memcached class and returns adds the servers specified
+	 * in the $memcached_servers global array.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.construct.php
+	 *
+	 * @param   null    $persistent_id      To create an instance that persists between requests, use persistent_id to specify a unique ID for the instance.
+	 */
+	public function __construct( $persistent_id = NULL ) {
+		global $memcached_servers, $blog_id, $table_prefix;
+
+		if ( is_null( $persistent_id ) || ! is_string( $persistent_id ) )
+			$this->m = new Memcached();
+		else
+			$this->m = new Memcached( $persistent_id );
+
+		if ( isset( $memcached_servers ) )
+			$this->servers = $memcached_servers;
+		else
+			$this->servers = array( array( '127.0.0.1', 11211 ) );
+
+		$this->addServers( $this->servers );
+
+		/**
+		 * This approach is borrowed from Sivel and Boren. Use the salt for easy cache invalidation and for
+		 * multi single WP installs on the same server.
+		 */
+		if ( ! defined( 'WP_CACHE_KEY_SALT' ) )
+			define( 'WP_CACHE_KEY_SALT', '' );
+
+		// Assign global and blog prefixes for use with keys
+		if ( function_exists( 'is_multisite' ) ) {
+			$this->global_prefix = ( is_multisite() || defined( 'CUSTOM_USER_TABLE' ) && defined( 'CUSTOM_USER_META_TABLE' ) ) ? '' : $table_prefix;
+			$this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ) . ':';
+		}
+
+		// Setup cacheable values for handling expiration times
+		$this->thirty_days = 60 * 60 * 24 * 30;
+		$this->now         = time();
+	}
+
+	/**
+	 * Adds a value to cache.
+	 *
+	 * If the specified key already exists, the value is not stored and the function
+	 * returns false.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.add.php
+	 *
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          The value to store.
+	 * @param   string      $group          The group value appended to the $key.
+	 * @param   int         $expiration     The expiration time, defaults to 0.
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   bool        $byKey          True to store in internal cache by key; false to not store by key
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function add( $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) {
+		/*
+		 * Ensuring that wp_suspend_cache_addition is defined before calling, because sometimes an advanced-cache.php
+		 * file will load object-cache.php before wp-includes/functions.php is loaded. In those cases, if wp_cache_add
+		 * is called in advanced-cache.php before any more of WordPress is loaded, we get a fatal error because
+		 * wp_suspend_cache_addition will not be defined until wp-includes/functions.php is loaded.
+		 */
+		if ( function_exists( 'wp_suspend_cache_addition' ) && wp_suspend_cache_addition() ) {
+			return false;
+		}
+
+		$derived_key = $this->buildKey( $key, $group );
+		$expiration  = $this->sanitize_expiration( $expiration );
+
+		// If group is a non-Memcached group, save to runtime cache, not Memcached
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+
+			// Add does not set the value if the key exists; mimic that here
+			if ( isset( $this->cache[$derived_key] ) )
+				return false;
+
+			$this->add_to_internal_cache( $derived_key, $value );
+
+			return true;
+		}
+
+		// Save to Memcached
+		if ( $byKey )
+			$result = $this->m->addByKey( $server_key, $derived_key, $value, $expiration );
+		else
+			$result = $this->m->add( $derived_key, $value, $expiration );
+
+		// Store in runtime cache if add was successful
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			$this->add_to_internal_cache( $derived_key, $value );
+
+		return $result;
+	}
+
+	/**
+	 * Adds a value to cache on a specific server.
+	 *
+	 * Using a server_key value, the object can be stored on a specified server as opposed
+	 * to a random server in the stack. Note that this method will add the key/value to the
+	 * _cache object as part of the runtime cache. It will add it to an array for the
+	 * specified server_key.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.addbykey.php
+	 *
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          The value to store.
+	 * @param   string      $group          The group value appended to the $key.
+	 * @param   int         $expiration     The expiration time, defaults to 0.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function addByKey( $server_key, $key, $value, $group = 'default', $expiration = 0 ) {
+		return $this->add( $key, $value, $group, $expiration, $server_key, true );
+	}
+
+	/**
+	 * Add a single server to the list of Memcached servers.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.addserver.php
+	 *
+	 * @param   string      $host           The hostname of the memcache server.
+	 * @param   int         $port           The port on which memcache is running.
+	 * @param   int         $weight         The weight of the server relative to the total weight of all the servers in the pool.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function addServer( $host, $port, $weight = 0 ) {
+		$host = is_string( $host ) ? $host : '127.0.0.1';
+		$port = is_numeric( $port ) && $port > 0 ? $port : 11211;
+		$weight = is_numeric( $weight ) && $weight > 0 ? $weight : 1;
+
+		return $this->m->addServer( $host, $port, $weight );
+	}
+
+	/**
+	 * Adds an array of servers to the pool.
+	 *
+	 * Each individual server in the array must include a domain and port, with an optional
+	 * weight value: $servers = array( array( '127.0.0.1', 11211, 0 ) );
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.addservers.php
+	 *
+	 * @param   array       $servers        Array of server to register.
+	 * @return  bool                        True on success; false on failure.
+	 */
+	public function addServers( $servers ) {
+		if ( ! is_object( $this->m ) )
+			return false;
+
+		return $this->m->addServers( $servers );
+	}
+
+	/**
+	 * Append data to an existing item.
+	 *
+	 * This method should throw an error if it is used with compressed data. This
+	 * is an expected behavior. Memcached casts the value to be appended to the initial value to the
+	 * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to
+	 * how memcached treats types, the behavior has been mimicked in the internal cache to produce
+	 * similar results and improve consistency. It is recommend that appends only occur with data of
+	 * the same type.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.append.php
+	 *
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          Must be string as appending mixed values is not well-defined.
+	 * @param   string      $group          The group value appended to the $key.
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   bool        $byKey          True to store in internal cache by key; false to not store by key
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function append( $key, $value, $group = 'default', $server_key = '', $byKey = false ) {
+		if ( ! is_string( $value ) && ! is_int( $value ) && ! is_float( $value ) )
+			return false;
+
+		$derived_key = $this->buildKey( $key, $group );
+
+		// If group is a non-Memcached group, append to runtime cache value, not Memcached
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			if ( ! isset( $this->cache[$derived_key] ) )
+				return false;
+
+			$combined = $this->combine_values( $this->cache[$derived_key], $value, 'app' );
+			$this->add_to_internal_cache( $derived_key, $combined );
+			return true;
+		}
+
+		// Append to Memcached value
+		if ( $byKey )
+			$result = $this->m->appendByKey( $server_key, $derived_key, $value );
+		else
+			$result = $this->m->append( $derived_key, $value );
+
+		// Store in runtime cache if add was successful
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() ) {
+			$combined = $this->combine_values( $this->cache[$derived_key], $value, 'app' );
+			$this->add_to_internal_cache( $derived_key, $combined );
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Append data to an existing item by server key.
+	 *
+	 * This method should throw an error if it is used with compressed data. This
+	 * is an expected behavior. Memcached casts the value to be appended to the initial value to the
+	 * type of the initial value. Be careful as this leads to unexpected behavior at times. Due to
+	 * how memcached treats types, the behavior has been mimicked in the internal cache to produce
+	 * similar results and improve consistency. It is recommend that appends only occur with data of
+	 * the same type.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.appendbykey.php
+	 *
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          Must be string as appending mixed values is not well-defined
+	 * @param   string      $group          The group value appended to the $key.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function appendByKey( $server_key, $key, $value, $group = 'default' ) {
+		return $this->append( $key, $value, $group, $server_key, true );
+	}
+
+	/**
+	 * Performs a "check and set" to store data.
+	 *
+	 * The set will be successful only if the no other request has updated the value since it was fetched since
+	 * this request.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.cas.php
+	 *
+	 * @param   float       $cas_token      Unique value associated with the existing item. Generated by memcached.
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          The value to store.
+	 * @param   string      $group          The group value appended to the $key.
+	 * @param   int         $expiration     The expiration time, defaults to 0.
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   bool        $byKey          True to store in internal cache by key; false to not store by key
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function cas( $cas_token, $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) {
+		$derived_key = $this->buildKey( $key, $group );
+		$expiration  = $this->sanitize_expiration( $expiration );
+
+		/**
+		 * If group is a non-Memcached group, save to runtime cache, not Memcached. Note
+		 * that since check and set cannot be emulated in the run time cache, this value
+		 * operation is treated as a normal "add" for no_mc_groups.
+		 */
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			$this->add_to_internal_cache( $derived_key, $value );
+			return true;
+		}
+
+		// Save to Memcached
+		if ( $byKey )
+			$result = $this->m->casByKey( $cas_token, $server_key, $derived_key, $value, $expiration );
+		else
+			$result = $this->m->cas( $cas_token, $derived_key, $value, $expiration );
+
+		// Store in runtime cache if cas was successful
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			$this->add_to_internal_cache( $derived_key, $value );
+
+		return $result;
+	}
+
+	/**
+	 * Performs a "check and set" to store data with a server key.
+	 *
+	 * The set will be successful only if the no other request has updated the value since it was fetched by
+	 * this request.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.casbykey.php
+	 *
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   float       $cas_token      Unique value associated with the existing item. Generated by memcached.
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          The value to store.
+	 * @param   string      $group          The group value appended to the $key.
+	 * @param   int         $expiration     The expiration time, defaults to 0.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function casByKey( $cas_token, $server_key, $key, $value, $group = 'default', $expiration = 0 ) {
+		return $this->cas( $cas_token, $key, $value, $group, $expiration, $server_key, true );
+	}
+
+	/**
+	 * Decrement a numeric item's value.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.decrement.php
+	 *
+	 * @param string    $key    The key under which to store the value.
+	 * @param int       $offset The amount by which to decrement the item's value.
+	 * @param string    $group  The group value appended to the $key.
+	 * @return int|bool         Returns item's new value on success or FALSE on failure.
+	 */
+	public function decrement( $key, $offset = 1, $group = 'default' ) {
+		$derived_key = $this->buildKey( $key, $group );
+
+		// Decrement values in no_mc_groups
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+
+			// Only decrement if the key already exists and value is 0 or greater (mimics memcached behavior)
+			if ( isset( $this->cache[$derived_key] ) && $this->cache[$derived_key] >= 0 ) {
+
+				// If numeric, subtract; otherwise, consider it 0 and do nothing
+				if ( is_numeric( $this->cache[$derived_key] ) )
+					$this->cache[$derived_key] -= (int) $offset;
+				else
+					$this->cache[$derived_key] = 0;
+
+				// Returned value cannot be less than 0
+				if ( $this->cache[$derived_key] < 0 )
+					$this->cache[$derived_key] = 0;
+
+				return $this->cache[$derived_key];
+			} else {
+				return false;
+			}
+		}
+
+		$result = $this->m->decrement( $derived_key, $offset );
+
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			$this->add_to_internal_cache( $derived_key, $result );
+
+		return $result;
+	}
+
+	/**
+	 * Decrement a numeric item's value.
+	 *
+	 * Alias for $this->decrement. Other caching backends use this abbreviated form of the function. It *may* cause
+	 * breakage somewhere, so it is nice to have. This function will also allow the core unit tests to pass.
+	 *
+	 * @param string    $key    The key under which to store the value.
+	 * @param int       $offset The amount by which to decrement the item's value.
+	 * @param string    $group  The group value appended to the $key.
+	 * @return int|bool         Returns item's new value on success or FALSE on failure.
+	 */
+	public function decr( $key, $offset = 1, $group = 'default' ) {
+		return $this->decrement( $key, $offset, $group );
+	}
+
+	/**
+	 * Remove the item from the cache.
+	 *
+	 * Remove an item from memcached with identified by $key after $time seconds. The
+	 * $time parameter allows an object to be queued for deletion without immediately
+	 * deleting. Between the time that it is queued and the time it's deleted, add,
+	 * replace, and get will fail, but set will succeed.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.delete.php
+	 *
+	 * @param   string      $key        The key under which to store the value.
+	 * @param   string      $group      The group value appended to the $key.
+	 * @param   int         $time       The amount of time the server will wait to delete the item in seconds.
+	 * @param   string      $server_key The key identifying the server to store the value on.
+	 * @param   bool        $byKey      True to store in internal cache by key; false to not store by key
+	 * @return  bool                    Returns TRUE on success or FALSE on failure.
+	 */
+	public function delete( $key, $group = 'default', $time = 0, $server_key = '', $byKey = false ) {
+		$derived_key = $this->buildKey( $key, $group );
+
+		// Remove from no_mc_groups array
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			if ( isset( $this->cache[$derived_key] ) )
+				unset( $this->cache[$derived_key] );
+
+			return true;
+		}
+
+		if ( $byKey )
+			$result = $this->m->deleteByKey( $server_key, $derived_key, $time );
+		else
+			$result = $this->m->delete( $derived_key, $time );
+
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			unset( $this->cache[$derived_key] );
+
+		return $result;
+	}
+
+	/**
+	 * Remove the item from the cache by server key.
+	 *
+	 * Remove an item from memcached with identified by $key after $time seconds. The
+	 * $time parameter allows an object to be queued for deletion without immediately
+	 * deleting. Between the time that it is queued and the time it's deleted, add,
+	 * replace, and get will fail, but set will succeed.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.deletebykey.php
+	 *
+	 * @param   string      $server_key The key identifying the server to store the value on.
+	 * @param   string      $key        The key under which to store the value.
+	 * @param   string      $group      The group value appended to the $key.
+	 * @param   int         $time       The amount of time the server will wait to delete the item in seconds.
+	 * @return  bool                    Returns TRUE on success or FALSE on failure.
+	 */
+	public function deleteByKey( $server_key, $key, $group = 'default', $time = 0 ) {
+		return $this->delete( $key, $group, $time, $server_key, true );
+	}
+
+	/**
+	 * Fetch the next result.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.fetch.php
+	 *
+	 * @return array|bool   Returns the next result or FALSE on failure.
+	 */
+	public function fetch() {
+		return $this->m->fetch();
+	}
+
+	/**
+	 * Fetch all remaining results from the last request.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.fetchall.php
+	 *
+	 * @return  array|bool          Returns the results or FALSE on failure.
+	 */
+	public function fetchAll() {
+	 	return $this->m->fetchAll();
+	}
+
+	/**
+	 * Invalidate all items in the cache.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.flush.php
+	 *
+	 * @param   int     $delay      Number of seconds to wait before invalidating the items.
+	 * @return  bool                Returns TRUE on success or FALSE on failure.
+	 */
+	public function flush( $delay = 0 ) {
+		$result = $this->m->flush( $delay );
+
+		// Only reset the runtime cache if memcached was properly flushed
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			$this->cache = array();
+
+		return $result;
+	}
+
+	/**
+	 * Retrieve object from cache.
+	 *
+	 * Gets an object from cache based on $key and $group. In order to fully support the $cache_cb and $cas_token
+	 * parameters, the runtime cache is ignored by this function if either of those values are set. If either of
+	 * those values are set, the request is made directly to the memcached server for proper handling of the
+	 * callback and/or token. Note that the $cas_token variable cannot be directly passed to the function. The
+	 * variable need to be first defined with a non null value.
+	 *
+	 * If using the $cache_cb argument, the new value will always have an expiration of time of 0 (forever). This
+	 * is a limitation of the Memcached PECL extension.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.get.php
+	 *
+	 * @param   string          $key        The key under which to store the value.
+	 * @param   string          $group      The group value appended to the $key.
+	 * @param   bool            $force      Whether or not to force a cache invalidation.
+	 * @param   null|bool       $found      Variable passed by reference to determine if the value was found or not.
+	 * @param   string          $server_key The key identifying the server to store the value on.
+	 * @param   bool            $byKey      True to store in internal cache by key; false to not store by key
+	 * @param   null|callable   $cache_cb   Read-through caching callback.
+	 * @param   null|float      $cas_token  The variable to store the CAS token in.
+	 * @return  bool|mixed                  Cached object value.
+	 */
+	public function get( $key, $group = 'default', $force = false, &$found = null, $server_key = '', $byKey = false, $cache_cb = NULL, &$cas_token = NULL ) {
+		$derived_key = $this->buildKey( $key, $group );
+
+		// Assume object is not found
+		$found = false;
+
+		// If either $cache_db, or $cas_token is set, must hit Memcached and bypass runtime cache
+		if ( func_num_args() > 6 && ! in_array( $group, $this->no_mc_groups ) ) {
+			if ( $byKey )
+				$value = $this->m->getByKey( $server_key, $derived_key, $cache_cb, $cas_token );
+			else
+				$value = $this->m->get( $derived_key, $cache_cb, $cas_token );
+		} else {
+			if ( isset( $this->cache[$derived_key] ) ) {
+				$found = true;
+				return is_object( $this->cache[$derived_key] ) ? clone $this->cache[$derived_key] : $this->cache[$derived_key];
+			} elseif ( in_array( $group, $this->no_mc_groups ) ) {
+				return false;
+			} else {
+				if ( $byKey )
+					$value = $this->m->getByKey( $server_key, $derived_key );
+				else
+					$value = $this->m->get( $derived_key );
+			}
+		}
+
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() ) {
+			$this->add_to_internal_cache( $derived_key, $value );
+			$found = true;
+		}
+
+		return is_object( $value ) ? clone $value : $value;
+	}
+
+	/**
+	 * Retrieve object from cache from specified server.
+	 *
+	 * Gets an object from cache based on $key, $group and $server_key. In order to fully support the $cache_cb and $cas_token
+	 * parameters, the runtime cache is ignored by this function if either of those values are set. If either of
+	 * those values are set, the request is made directly to the memcached server for proper handling of the
+	 * callback and/or token. Note that the $cas_token variable cannot be directly passed to the function. The
+	 * variable need to be first defined with a non null value.
+	 *
+	 * If using the $cache_cb argument, the new value will always have an expiration of time of 0 (forever). This
+	 * is a limitation of the Memcached PECL extension.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.getbykey.php
+	 *
+	 * @param   string          $server_key The key identifying the server to store the value on.
+	 * @param   string          $key        The key under which to store the value.
+	 * @param   string          $group      The group value appended to the $key.
+	 * @param   bool            $force      Whether or not to force a cache invalidation.
+	 * @param   null|bool       $found      Variable passed by reference to determine if the value was found or not.
+	 * @param   null|string     $cache_cb   Read-through caching callback.
+	 * @param   null|float      $cas_token  The variable to store the CAS token in.
+	 * @return  bool|mixed                  Cached object value.
+	 */
+	public function getByKey( $server_key, $key, $group = 'default', $force = false, &$found = null, $cache_cb = NULL, &$cas_token = NULL ) {
+		/**
+		 * Need to be careful how "get" is called. If you send $cache_cb, and $cas_token, it will hit memcached.
+		 * Only send those args if they were sent to this function.
+		 */
+		if ( func_num_args() > 5 )
+			return $this->get( $key, $group, $force, $found, $server_key, true, $cache_cb, $cas_token );
+		else
+			return $this->get( $key, $group, $force, $found, $server_key, true );
+	}
+
+	/**
+	 * Request multiple keys without blocking.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.getdelayed.php
+	 *
+	 * @param   string|array    $keys       Array or string of key(s) to request.
+	 * @param   string|array    $groups     Array or string of group(s) for the key(s). See buildKeys for more on how these are handled.
+	 * @param   bool            $with_cas   Whether to request CAS token values also.
+	 * @param   null            $value_cb   The result callback or NULL.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function getDelayed( $keys, $groups = 'default', $with_cas = false, $value_cb = NULL ) {
+		$derived_keys = $this->buildKeys( $keys, $groups );
+		return $this->m->getDelayed( $derived_keys, $with_cas, $value_cb );
+	}
+
+	/**
+	 * Request multiple keys without blocking from a specified server.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.getdelayed.php
+	 *
+	 * @param   string          $server_key The key identifying the server to store the value on.
+	 * @param   string|array    $keys       Array or string of key(s) to request.
+	 * @param   string|array    $groups     Array or string of group(s) for the key(s). See buildKeys for more on how these are handled.
+	 * @param   bool            $with_cas   Whether to request CAS token values also.
+	 * @param   null            $value_cb   The result callback or NULL.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function getDelayedByKey( $server_key, $keys, $groups = 'default', $with_cas = false, $value_cb = NULL ) {
+		$derived_keys = $this->buildKeys( $keys, $groups );
+		return $this->m->getDelayedByKey( $server_key, $derived_keys, $with_cas, $value_cb );
+	}
+
+	/**
+	 * Gets multiple values from memcached in one request.
+	 *
+	 * See the buildKeys method definition to understand the $keys/$groups parameters.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.getmulti.php
+	 *
+	 * @param   array           $keys       Array of keys to retrieve.
+	 * @param   string|array    $groups     If string, used for all keys. If arrays, corresponds with the $keys array.
+	 * @param   string          $server_key The key identifying the server to store the value on.
+	 * @param   null|array      $cas_tokens The variable to store the CAS tokens for the found items.
+	 * @param   int             $flags      The flags for the get operation.
+	 * @return  bool|array                  Returns the array of found items or FALSE on failure.
+	 */
+	public function getMulti( $keys, $groups = 'default', $server_key = '', &$cas_tokens = NULL, $flags = NULL ) {
+		$derived_keys = $this->buildKeys( $keys, $groups );
+
+		/**
+		 * If either $cas_tokens, or $flags is set, must hit Memcached and bypass runtime cache. Note that
+		 * this will purposely ignore no_mc_groups values as they cannot handle CAS tokens or the special
+		 * flags; however, if the groups of groups contains a no_mc_group, this is bypassed.
+		 */
+		if ( func_num_args() > 3 && ! $this->contains_no_mc_group( $groups ) ) {
+			if ( ! empty( $server_key ) )
+				$values = $this->m->getMultiByKey( $server_key, $derived_keys, $cas_tokens, $flags );
+			else
+				$values = $this->m->getMulti( $derived_keys, $cas_tokens, $flags );
+		} else {
+			$values = array();
+			$need_to_get = array();
+
+			// Pull out values from runtime cache, or mark for retrieval
+			foreach ( $derived_keys as $key ) {
+				if ( isset( $this->cache[$key] ) )
+					$values[$key] = $this->cache[$key];
+				else
+					$need_to_get[$key] = $key;
+			}
+
+			// Get those keys not found in the runtime cache
+			if ( ! empty( $need_to_get ) ) {
+				if ( ! empty( $server_key ) )
+					$result = $this->m->getMultiByKey( $server_key, array_keys( $need_to_get ) );
+				else
+					$result = $this->m->getMulti( array_keys( $need_to_get ) );
+			}
+
+			// Merge with values found in runtime cache
+			if ( isset( $result ) && Memcached::RES_SUCCESS === $this->getResultCode() )
+				$values = array_merge( $values, $result );
+
+			// If order should be preserved, reorder now
+			if ( ! empty( $need_to_get ) && $flags === Memcached::GET_PRESERVE_ORDER ) {
+				$ordered_values = array();
+
+				foreach ( $derived_keys as $key ) {
+					if ( isset( $values[$key] ) )
+						$ordered_values[$key] = $values[$key];
+				}
+
+				$values = $ordered_values;
+				unset( $ordered_values );
+			}
+		}
+
+		// Add the values to the runtime cache
+		$this->cache = array_merge( $this->cache, $values );
+
+		return $values;
+	}
+
+	/**
+	 * Gets multiple values from memcached in one request by specified server key.
+	 *
+	 * See the buildKeys method definition to understand the $keys/$groups parameters.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.getmultibykey.php
+	 *
+	 * @param   string          $server_key The key identifying the server to store the value on.
+	 * @param   array           $keys       Array of keys to retrieve.
+	 * @param   string|array    $groups     If string, used for all keys. If arrays, corresponds with the $keys array.
+	 * @param   null|array      $cas_tokens The variable to store the CAS tokens for the found items.
+	 * @param   int             $flags      The flags for the get operation.
+	 * @return  bool|array                  Returns the array of found items or FALSE on failure.
+	 */
+	public function getMultiByKey( $server_key, $keys, $groups = 'default', &$cas_tokens = NULL, $flags = NULL ) {
+		/**
+		 * Need to be careful how "getMulti" is called. If you send $cache_cb, and $cas_token, it will hit memcached.
+		 * Only send those args if they were sent to this function.
+		 */
+		if ( func_num_args() > 3 )
+			return $this->getMulti( $keys, $groups, $server_key, $cas_tokens, $flags );
+		else
+			return $this->getMulti( $keys, $groups, $server_key );
+	}
+
+	/**
+	 * Retrieve a Memcached option value.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.getoption.php
+	 *
+	 * @param   int         $option     One of the Memcached::OPT_* constants.
+	 * @return  mixed                   Returns the value of the requested option, or FALSE on error.
+	 */
+	public function getOption( $option ) {
+		return $this->m->getOption( $option );
+	}
+
+	/**
+	 * Return the result code of the last option.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.getresultcode.php
+	 *
+	 * @return  int     Result code of the last Memcached operation.
+	 */
+	public function getResultCode() {
+ 	    return $this->m->getResultCode();
+	}
+
+	/**
+	 * Return the message describing the result of the last operation.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.getresultmessage.php
+	 *
+	 * @return  string      Message describing the result of the last Memcached operation.
+	 */
+	public function getResultMessage() {
+   	    return $this->m->getResultMessage();
+	}
+
+	/**
+	 * Get server information by key.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.getserverbykey.php
+	 *
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @return  array                       Array with host, post, and weight on success, FALSE on failure.
+	 */
+	public function getServerByKey( $server_key ) {
+		return $this->m->getServerByKey( $server_key );
+	}
+
+	/**
+	 * Get the list of servers in the pool.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.getserverlist.php
+	 *
+	 * @return  array       The list of all servers in the server pool.
+	 */
+	public function getServerList() {
+		return $this->m->getServerList();
+	}
+
+	/**
+     * Get server pool statistics.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.getstats.php
+	 *
+	 * @return  array       Array of server statistics, one entry per server.
+	 */
+	public function getStats() {
+		return $this->m->getStats();
+	}
+
+	/**
+	 * Get server pool memcached version information.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.getversion.php
+	 *
+	 * @return  array       Array of server versions, one entry per server.
+	 */
+	public function getVersion() {
+		return $this->m->getVersion();
+	}
+
+	/**
+	 * Increment a numeric item's value.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.increment.php
+	 *
+	 * @param   string      $key        The key under which to store the value.
+	 * @param   int         $offset     The amount by which to increment the item's value.
+	 * @param   string      $group      The group value appended to the $key.
+	 * @return  int|bool                Returns item's new value on success or FALSE on failure.
+	 */
+	public function increment( $key, $offset = 1, $group = 'default' ) {
+		$derived_key = $this->buildKey( $key, $group );
+
+		// Increment values in no_mc_groups
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+
+			// Only increment if the key already exists and the number is currently 0 or greater (mimics memcached behavior)
+			if ( isset( $this->cache[$derived_key] ) &&  $this->cache[$derived_key] >= 0 ) {
+
+				// If numeric, add; otherwise, consider it 0 and do nothing
+				if ( is_numeric( $this->cache[$derived_key] ) )
+					$this->cache[$derived_key] += (int) $offset;
+				else
+					$this->cache[$derived_key] = 0;
+
+				// Returned value cannot be less than 0
+				if ( $this->cache[$derived_key] < 0 )
+					$this->cache[$derived_key] = 0;
+
+				return $this->cache[$derived_key];
+			} else {
+				return false;
+			}
+		}
+
+		$result = $this->m->increment( $derived_key, $offset );
+
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			$this->add_to_internal_cache( $derived_key, $result );
+
+		return $result;
+	}
+
+	/**
+	 * Synonymous with $this->incr.
+	 *
+	 * Certain plugins expect an "incr" method on the $wp_object_cache object (e.g., Batcache). Since the original
+	 * version of this library matched names to the memcached methods, the "incr" method was missing. Adding this
+	 * method restores compatibility with plugins expecting an "incr" method.
+	 *
+	 * @param   string      $key        The key under which to store the value.
+	 * @param   int         $offset     The amount by which to increment the item's value.
+	 * @param   string      $group      The group value appended to the $key.
+	 * @return  int|bool                Returns item's new value on success or FALSE on failure.
+	 */
+	public function incr( $key, $offset = 1, $group = 'default' ) {
+		return $this->increment( $key, $offset, $group );
+	}
+
+	/**
+	 * Prepend data to an existing item.
+	 *
+	 * This method should throw an error if it is used with compressed data. This is an expected behavior.
+	 * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be
+	 * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to
+	 * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer"
+	 * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been
+	 * mimicked in the internal cache to produce similar results and improve consistency. It is recommend
+	 * that prepends only occur with data of the same type.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.prepend.php
+	 *
+	 * @param   string    $key          The key under which to store the value.
+	 * @param   string    $value        Must be string as prepending mixed values is not well-defined.
+	 * @param   string    $group        The group value prepended to the $key.
+	 * @param   string    $server_key   The key identifying the server to store the value on.
+	 * @param   bool      $byKey        True to store in internal cache by key; false to not store by key
+	 * @return  bool                    Returns TRUE on success or FALSE on failure.
+	 */
+	public function prepend( $key, $value, $group = 'default', $server_key = '', $byKey = false ) {
+		if ( ! is_string( $value ) && ! is_int( $value ) && ! is_float( $value ) )
+			return false;
+
+		$derived_key = $this->buildKey( $key, $group );
+
+		// If group is a non-Memcached group, prepend to runtime cache value, not Memcached
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			if ( ! isset( $this->cache[$derived_key] ) )
+				return false;
+
+			$combined = $this->combine_values( $this->cache[$derived_key], $value, 'pre' );
+			$this->add_to_internal_cache( $derived_key, $combined );
+			return true;
+		}
+
+		// Append to Memcached value
+		if ( $byKey )
+			$result = $this->m->prependByKey( $server_key, $derived_key, $value );
+		else
+			$result = $this->m->prepend( $derived_key, $value );
+
+		// Store in runtime cache if add was successful
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() ) {
+			$combined = $this->combine_values( $this->cache[$derived_key], $value, 'pre' );
+			$this->add_to_internal_cache( $derived_key, $combined );
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Append data to an existing item by server key.
+	 *
+	 * This method should throw an error if it is used with compressed data. This is an expected behavior.
+	 * Memcached casts the value to be prepended to the initial value to the type of the initial value. Be
+	 * careful as this leads to unexpected behavior at times. For instance, prepending (float) 45.23 to
+	 * (int) 23 will result in 45, because the value is first combined (45.2323) then cast to "integer"
+	 * (the original value), which will be (int) 45. Due to how memcached treats types, the behavior has been
+	 * mimicked in the internal cache to produce similar results and improve consistency. It is recommend
+	 * that prepends only occur with data of the same type.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.prependbykey.php
+	 *
+	 * @param   string    $server_key   The key identifying the server to store the value on.
+	 * @param   string    $key          The key under which to store the value.
+	 * @param   string    $value        Must be string as prepending mixed values is not well-defined.
+	 * @param   string    $group        The group value prepended to the $key.
+	 * @return  bool                    Returns TRUE on success or FALSE on failure.
+	 */
+	public function prependByKey( $server_key, $key, $value, $group = 'default' ) {
+		return $this->prepend( $key, $value, $group, $server_key, true );
+	}
+
+	/**
+	 * Replaces a value in cache.
+	 *
+	 * This method is similar to "add"; however, is does not successfully set a value if
+	 * the object's key is not already set in cache.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.replace.php
+	 *
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          The value to store.
+	 * @param   string      $group          The group value appended to the $key.
+	 * @param   bool        $byKey          True to store in internal cache by key; false to not store by key
+	 * @param   int         $expiration     The expiration time, defaults to 0.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function replace( $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) {
+		$derived_key = $this->buildKey( $key, $group );
+		$expiration  = $this->sanitize_expiration( $expiration );
+
+		// If group is a non-Memcached group, save to runtime cache, not Memcached
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+
+			// Replace won't save unless the key already exists; mimic this behavior here
+			if ( ! isset( $this->cache[$derived_key] ) )
+				return false;
+
+			$this->cache[$derived_key] = $value;
+			return true;
+		}
+
+		// Save to Memcached
+		if ( $byKey )
+			$result = $this->m->replaceByKey( $server_key, $derived_key, $value, $expiration );
+		else
+			$result = $this->m->replace( $derived_key, $value, $expiration );
+
+		// Store in runtime cache if add was successful
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			$this->add_to_internal_cache( $derived_key, $value );
+
+		return $result;
+	}
+
+	/**
+	 * Replaces a value in cache on a specific server.
+	 *
+	 * This method is similar to "addByKey"; however, is does not successfully set a value if
+	 * the object's key is not already set in cache.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.addbykey.php
+	 *
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          The value to store.
+	 * @param   string      $group          The group value appended to the $key.
+	 * @param   int         $expiration     The expiration time, defaults to 0.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function replaceByKey( $server_key, $key, $value, $group = 'default', $expiration = 0 ) {
+		return $this->replace( $key, $value, $group, $expiration, $server_key, true );
+	}
+
+	/**
+	 * Sets a value in cache.
+	 *
+	 * The value is set whether or not this key already exists in memcached.
+	 *
+	 * @link http://www.php.net/manual/en/memcached.set.php
+	 *
+	 * @param   string      $key        The key under which to store the value.
+	 * @param   mixed       $value      The value to store.
+	 * @param   string      $group      The group value appended to the $key.
+	 * @param   int         $expiration The expiration time, defaults to 0.
+	 * @param   string      $server_key The key identifying the server to store the value on.
+	 * @param   bool        $byKey      True to store in internal cache by key; false to not store by key
+	 * @return  bool                    Returns TRUE on success or FALSE on failure.
+	 */
+	public function set( $key, $value, $group = 'default', $expiration = 0, $server_key = '', $byKey = false ) {
+		$derived_key = $this->buildKey( $key, $group );
+		$expiration  = $this->sanitize_expiration( $expiration );
+
+		// If group is a non-Memcached group, save to runtime cache, not Memcached
+		if ( in_array( $group, $this->no_mc_groups ) ) {
+			$this->add_to_internal_cache( $derived_key, $value );
+			return true;
+		}
+
+		// Save to Memcached
+		if ( $byKey ) {
+			$result = $this->m->setByKey( $server_key, $derived_key, $value, $expiration );
+		} else {
+			$result = $this->m->set( $derived_key, $value, $expiration );
+		}
+
+		// Store in runtime cache if add was successful
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			$this->add_to_internal_cache( $derived_key, $value );
+
+		return $result;
+	}
+
+	/**
+	 * Sets a value in cache on a specific server.
+	 *
+	 * The value is set whether or not this key already exists in memcached.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.setbykey.php
+	 *
+	 * @param   string      $server_key     The key identifying the server to store the value on.
+	 * @param   string      $key            The key under which to store the value.
+	 * @param   mixed       $value          The value to store.
+	 * @param   string      $group          The group value appended to the $key.
+	 * @param   int         $expiration     The expiration time, defaults to 0.
+	 * @return  bool                        Returns TRUE on success or FALSE on failure.
+	 */
+	public function setByKey( $server_key, $key, $value, $group = 'default', $expiration = 0 ) {
+		return $this->set( $key, $value, $group, $expiration, $server_key, true );
+	}
+
+	/**
+	 * Set multiple values to cache at once.
+	 *
+	 * By sending an array of $items to this function, all values are saved at once to
+	 * memcached, reducing the need for multiple requests to memcached. The $items array
+	 * keys and values are what are stored to memcached. The keys in the $items array
+	 * are merged with the $groups array/string value via buildKeys to determine the
+	 * final key for the object.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.setmulti.php
+	 *
+	 * @param   array           $items          An array of key/value pairs to store on the server.
+	 * @param   string|array    $groups         Group(s) to merge with key(s) in $items.
+	 * @param   int             $expiration     The expiration time, defaults to 0.
+	 * @param   string          $server_key     The key identifying the server to store the value on.
+	 * @param   bool            $byKey          True to store in internal cache by key; false to not store by key
+	 * @return  bool                            Returns TRUE on success or FALSE on failure.
+	 */
+	public function setMulti( $items, $groups = 'default', $expiration = 0, $server_key = '', $byKey = false ) {
+		// Build final keys and replace $items keys with the new keys
+		$derived_keys  = $this->buildKeys( array_keys( $items ), $groups );
+		$expiration    = $this->sanitize_expiration( $expiration );
+		$derived_items = array_combine( $derived_keys, $items );
+
+		// Do not add to memcached if in no_mc_groups
+		foreach ( $derived_items as $derived_key => $value ) {
+
+			// Get the individual item's group
+			$key_pieces = explode( ':', $derived_key );
+
+			// If group is a non-Memcached group, save to runtime cache, not Memcached
+			if ( in_array( $key_pieces[1], $this->no_mc_groups ) ) {
+				$this->add_to_internal_cache( $derived_key, $value );
+				unset( $derived_items[$derived_key] );
+			}
+		}
+
+		// Save to memcached
+		if ( $byKey )
+			$result = $this->m->setMultiByKey( $server_key, $derived_items, $expiration );
+		else
+			$result = $this->m->setMulti( $derived_items, $expiration );
+
+		// Store in runtime cache if add was successful
+		if ( Memcached::RES_SUCCESS === $this->getResultCode() )
+			$this->cache = array_merge( $this->cache, $derived_items );
+
+		return $result;
+	}
+
+	/**
+	 * Set multiple values to cache at once on specified server.
+	 *
+	 * By sending an array of $items to this function, all values are saved at once to
+	 * memcached, reducing the need for multiple requests to memcached. The $items array
+	 * keys and values are what are stored to memcached. The keys in the $items array
+	 * are merged with the $groups array/string value via buildKeys to determine the
+	 * final key for the object.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.setmultibykey.php
+	 *
+	 * @param   string          $server_key     The key identifying the server to store the value on.
+	 * @param   array           $items          An array of key/value pairs to store on the server.
+	 * @param   string|array    $groups         Group(s) to merge with key(s) in $items.
+	 * @param   int             $expiration     The expiration time, defaults to 0.
+	 * @return  bool                            Returns TRUE on success or FALSE on failure.
+	 */
+	public function setMultiByKey( $server_key, $items, $groups = 'default', $expiration = 0 ) {
+		return $this->setMulti( $items, $groups, $expiration, $server_key, true );
+	}
+
+	/**
+	 * Set a Memcached option.
+	 *
+	 * @link    http://www.php.net/manual/en/memcached.setoption.php
+	 *
+	 * @param   int         $option     Option name.
+	 * @param   mixed       $value      Option value.
+	 * @return  bool                Returns TRUE on success or FALSE on failure.
+	 */
+	public function setOption( $option, $value ) {
+		return $this->m->setOption( $option, $value );
+	}
+
+	/**
+	 * Builds a key for the cached object using the blog_id, key, and group values.
+	 *
+	 * @author  Ryan Boren   This function is inspired by the original WP Memcached Object cache.
+	 * @link    http://wordpress.org/extend/plugins/memcached/
+	 *
+	 * @param   string      $key        The key under which to store the value.
+	 * @param   string      $group      The group value appended to the $key.
+	 * @return  string
+	 */
+	public function buildKey( $key, $group = 'default' ) {
+		if ( empty( $group ) )
+			$group = 'default';
+
+		if ( false !== array_search( $group, $this->global_groups ) )
+			$prefix = $this->global_prefix;
+		else
+			$prefix = $this->blog_prefix;
+
+		return preg_replace( '/\s+/', '', WP_CACHE_KEY_SALT . "$prefix$group:$key" );
+	}
+
+	/**
+	 * Creates an array of keys from passed key(s) and group(s).
+	 *
+	 * This function takes a string or array of key(s) and group(s) and combines them into a single dimensional
+	 * array that merges the keys and groups. If the same number of keys and groups exist, the final keys will
+	 * append $groups[n] to $keys[n]. If there are more keys than groups and the $groups parameter is an array,
+	 * $keys[n] will be combined with $groups[n] until $groups runs out of values. 'default' will be used for remaining
+	 * values. If $keys is an array and $groups is a string, all final values will append $groups to $keys[n].
+	 * If both values are strings, they will be combined into a single string. Note that if more $groups are received
+	 * than $keys, the method will return an empty array. This method is primarily a helper method for methods
+	 * that call memcached with an array of keys.
+	 *
+	 * @param   string|array    $keys       Key(s) to merge with group(s).
+	 * @param   string|array    $groups     Group(s) to merge with key(s).
+	 * @return  array                       Array that combines keys and groups into a single set of memcached keys.
+	 */
+	public function buildKeys( $keys, $groups = 'default' ) {
+		$derived_keys = array();
+
+		// If strings sent, convert to arrays for proper handling
+		if ( ! is_array( $groups ) )
+			$groups = (array) $groups;
+
+		if ( ! is_array( $keys ) )
+			$keys = (array) $keys;
+
+		// If we have equal numbers of keys and groups, merge $keys[n] and $group[n]
+		if ( count( $keys ) == count( $groups ) ) {
+			for ( $i = 0; $i < count( $keys ); $i++ ) {
+				$derived_keys[] = $this->buildKey( $keys[$i], $groups[$i] );
+			}
+
+		// If more keys are received than groups, merge $keys[n] and $group[n] until no more group are left; remaining groups are 'default'
+		} elseif ( count( $keys ) > count( $groups ) ) {
+			for ( $i = 0; $i < count( $keys ); $i++ ) {
+				if ( isset( $groups[$i] ) )
+					$derived_keys[] = $this->buildKey( $keys[$i], $groups[$i] );
+				elseif ( count( $groups ) == 1 )
+					$derived_keys[] = $this->buildKey( $keys[$i], $groups[0] );
+				else
+					$derived_keys[] = $this->buildKey( $keys[$i], 'default' );
+			}
+		}
+
+		return $derived_keys;
+	}
+
+	/**
+	 * Ensure that a proper expiration time is set.
+	 *
+	 * Memcached treats any value over 30 days as a timestamp. If a developer sets the expiration for greater than 30
+	 * days or less than the current timestamp, the timestamp is in the past and the value isn't cached. This function
+	 * detects values in that range and corrects them.
+	 *
+	 * @param  string|int    $expiration    The dirty expiration time.
+	 * @return string|int                   The sanitized expiration time.
+	 */
+	public function sanitize_expiration( $expiration ) {
+		if ( $expiration > $this->thirty_days && $expiration <= $this->now ) {
+			$expiration = $expiration + $this->now;
+		}
+
+		return $expiration;
+	}
+
+	/**
+	 * Concatenates two values and casts to type of the first value.
+	 *
+	 * This is used in append and prepend operations to match how these functions are handled
+	 * by memcached. In both cases, whichever value is the original value in the combined value
+	 * will dictate the type of the combined value.
+	 *
+	 * @param   mixed       $original   Original value that dictates the combined type.
+	 * @param   mixed       $pended     Value to combine with original value.
+	 * @param   string      $direction  Either 'pre' or 'app'.
+	 * @return  mixed                   Combined value casted to the type of the first value.
+	 */
+	public function combine_values( $original, $pended, $direction ) {
+		$type = gettype( $original );
+
+		// Combine the values based on direction of the "pend"
+		if ( 'pre' == $direction )
+			$combined = $pended . $original;
+		else
+			$combined = $original . $pended;
+
+		// Cast type of combined value
+		settype( $combined, $type );
+
+		return $combined;
+	}
+
+	/**
+	 * Simple wrapper for saving object to the internal cache.
+	 *
+	 * @param   string      $derived_key    Key to save value under.
+	 * @param   mixed       $value          Object value.
+	 */
+	public function add_to_internal_cache( $derived_key, $value ) {
+		if ( is_object( $value ) ) {
+			$value = clone $value;
+		}
+
+		$this->cache[$derived_key] = $value;
+	}
+
+	/**
+	 * Determines if a no_mc_group exists in a group of groups.
+	 *
+	 * @param   mixed   $groups     The groups to search.
+	 * @return  bool                True if a no_mc_group is present; false if a no_mc_group is not present.
+	 */
+	public function contains_no_mc_group( $groups ) {
+		if ( is_scalar( $groups ) )
+			return in_array( $groups, $this->no_mc_groups );
+
+		if ( ! is_array( $groups ) )
+			return false;
+
+		foreach ( $groups as $group ) {
+			if ( in_array( $group, $this->no_mc_groups ) )
+				return true;
+		}
+
+		return false;
+	}
+
+	/**
+	 * Add global groups.
+	 *
+	 * @author  Ryan Boren   This function comes straight from the original WP Memcached Object cache
+	 * @link    http://wordpress.org/extend/plugins/memcached/
+	 *
+	 * @param   array       $groups     Array of groups.
+	 * @return  void
+	 */
+	public function add_global_groups( $groups ) {
+		if ( ! is_array( $groups ) )
+			$groups = (array) $groups;
+
+		$this->global_groups = array_merge( $this->global_groups, $groups);
+		$this->global_groups = array_unique( $this->global_groups );
+	}
+
+	/**
+	 * Add non-persistent groups.
+	 *
+	 * @author  Ryan Boren   This function comes straight from the original WP Memcached Object cache
+	 * @link    http://wordpress.org/extend/plugins/memcached/
+	 *
+	 * @param   array       $groups     Array of groups.
+	 * @return  void
+	 */
+	public function add_non_persistent_groups( $groups ) {
+		if ( ! is_array( $groups ) )
+			$groups = (array) $groups;
+
+		$this->no_mc_groups = array_merge( $this->no_mc_groups, $groups );
+		$this->no_mc_groups = array_unique( $this->no_mc_groups );
+	}
+
+	/**
+	 * Get a value specifically from the internal, run-time cache, not memcached.
+	 *
+	 * @param   int|string  $key        Key value.
+	 * @param   int|string  $group      Group that the value belongs to.
+	 * @return  bool|mixed              Value on success; false on failure.
+	 */
+	public function get_from_runtime_cache( $key, $group ) {
+		$derived_key = $this->buildKey( $key, $group );
+
+		if ( isset( $this->cache[$derived_key] ) )
+			return $this->cache[$derived_key];
+
+		return false;
+	}
+
+	/**
+	 * Switch blog prefix, which changes the cache that is accessed.
+	 *
+	 * @param  int     $blog_id    Blog to switch to.
+	 * @return void
+	 */
+	public function switch_to_blog( $blog_id ) {
+		global $table_prefix;
+		$blog_id           = (int) $blog_id;
+		$this->blog_prefix = ( is_multisite() ? $blog_id : $table_prefix ) . ':';
+	}
+}
diff --git a/tests/phpunit/includes/phpunit6-compat.php b/tests/phpunit/includes/phpunit6-compat.php
new file mode 100644
index 0000000..a5b0a9f
--- /dev/null
+++ b/tests/phpunit/includes/phpunit6-compat.php
@@ -0,0 +1,37 @@
+<?php
+
+if ( class_exists( 'PHPUnit\Runner\Version' ) && version_compare( PHPUnit\Runner\Version::id(), '6.0', '>=' ) ) {
+
+	class_alias( 'PHPUnit\Framework\TestCase',                   'PHPUnit_Framework_TestCase' );
+	class_alias( 'PHPUnit\Framework\Exception',                  'PHPUnit_Framework_Exception' );
+	class_alias( 'PHPUnit\Framework\ExpectationFailedException', 'PHPUnit_Framework_ExpectationFailedException' );
+	class_alias( 'PHPUnit\Framework\Error\Notice',               'PHPUnit_Framework_Error_Notice' );
+	class_alias( 'PHPUnit\Framework\Test',                       'PHPUnit_Framework_Test' );
+	class_alias( 'PHPUnit\Framework\Warning',                    'PHPUnit_Framework_Warning' );
+	class_alias( 'PHPUnit\Framework\AssertionFailedError',       'PHPUnit_Framework_AssertionFailedError' );
+	class_alias( 'PHPUnit\Framework\TestSuite',                  'PHPUnit_Framework_TestSuite' );
+	class_alias( 'PHPUnit\Framework\TestListener',               'PHPUnit_Framework_TestListener' );
+	class_alias( 'PHPUnit\Util\GlobalState',                     'PHPUnit_Util_GlobalState' );
+	class_alias( 'PHPUnit\Util\Getopt',                          'PHPUnit_Util_Getopt' );
+
+	class PHPUnit_Util_Test extends PHPUnit\Util\Test {
+
+		public static function getTickets( $className, $methodName ) {
+			$annotations = self::parseTestMethodAnnotations( $className, $methodName );
+
+			$tickets = array();
+
+			if ( isset( $annotations['class']['ticket'] ) ) {
+				$tickets = $annotations['class']['ticket'];
+			}
+
+			if ( isset( $annotations['method']['ticket'] ) ) {
+				$tickets = array_merge( $tickets, $annotations['method']['ticket'] );
+			}
+
+			return array_unique( $tickets );
+		}
+
+	}
+
+}
diff --git a/tests/phpunit/includes/testcase-ajax.php b/tests/phpunit/includes/testcase-ajax.php
index 787e6bf..19a87b2 100644
--- a/tests/phpunit/includes/testcase-ajax.php
+++ b/tests/phpunit/includes/testcase-ajax.php
@@ -200,4 +200,25 @@ protected function _handleAjax( $action ) {
 		if ( !empty( $buffer ) )
 			$this->_last_response = $buffer;
 	}
+
+	/**
+	 * PHPUnit 6+ compatibility shim.
+	 *
+	 * @param mixed      $exception
+	 * @param string     $message
+	 * @param int|string $code
+	 */
+	public function setExpectedException( $exception, $message = '', $code = null ) {
+		if ( is_callable( 'parent::setExpectedException' ) ) {
+			parent::setExpectedException( $exception, $message, $code );
+		} else {
+			$this->expectException( $exception );
+			if ( '' !== $message ) {
+				$this->expectExceptionMessage( $message );
+			}
+			if ( null !== $code ) {
+				$this->expectExceptionCode( $code );
+			}
+		}
+	}
 }
diff --git a/tests/phpunit/includes/testcase-canonical.php b/tests/phpunit/includes/testcase-canonical.php
index e0b93c8..a48d716 100644
--- a/tests/phpunit/includes/testcase-canonical.php
+++ b/tests/phpunit/includes/testcase-canonical.php
@@ -157,9 +157,6 @@ public static function delete_shared_fixtures() {
 	public function assertCanonical( $test_url, $expected, $ticket = 0, $expected_doing_it_wrong = array() ) {
 		$this->expected_doing_it_wrong = array_merge( $this->expected_doing_it_wrong, (array) $expected_doing_it_wrong );
 
-		if ( $ticket )
-			$this->knownWPBug( $ticket );
-
 		$ticket_ref = ($ticket > 0) ? 'Ticket #' . $ticket : null;
 
 		if ( is_string($expected) )
@@ -168,7 +165,7 @@ public function assertCanonical( $test_url, $expected, $ticket = 0, $expected_do
 			$expected = array( 'qv' => $expected );
 
 		if ( !isset($expected['url']) && !isset($expected['qv']) )
-			$this->markTestSkipped('No valid expected output was provided');
+			$this->fail( 'No valid expected output was provided' );
 
 		$this->go_to( home_url( $test_url ) );
 
@@ -181,6 +178,7 @@ public function assertCanonical( $test_url, $expected, $ticket = 0, $expected_do
 			$this->assertEquals( $expected['url'], $parsed_can_url['path'] . (!empty($parsed_can_url['query']) ? '?' . $parsed_can_url['query'] : ''), $ticket_ref );
 		}
 
+		// If the test data doesn't include expected query vars, then we're done here
 		if ( ! isset($expected['qv']) )
 			return;
 
diff --git a/tests/phpunit/includes/testcase.php b/tests/phpunit/includes/testcase.php
index c77511d..ba1c47f 100644
--- a/tests/phpunit/includes/testcase.php
+++ b/tests/phpunit/includes/testcase.php
@@ -198,6 +198,28 @@ public function skipOnAutomatedBranches() {
 	}
 
 	/**
+	 * Allow tests to be skipped when Multisite is not in use.
+	 *
+	 * Use in conjunction with the ms-required group.
+	 */
+	public function skipWithoutMultisite() {
+		if ( ! is_multisite() ) {
+			$this->markTestSkipped( 'Test only runs on Multisite' );
+		}
+	}
+
+	/**
+	 * Allow tests to be skipped when Multisite is in use.
+	 *
+	 * Use in conjunction with the ms-excluded group.
+	 */
+	public function skipWithMultisite() {
+		if ( is_multisite() ) {
+			$this->markTestSkipped( 'Test does not run on Multisite' );
+		}
+	}
+
+	/**
 	 * Unregister existing post types and register defaults.
 	 *
 	 * Run before each test in order to clean up the global scope, in case
@@ -388,8 +410,12 @@ function expectedDeprecated() {
 			$errors[] = "Unexpected incorrect usage notice for $unexpected";
 		}
 
-		if ( ! empty( $errors ) ) {
-			$this->fail( implode( "\n", $errors ) );
+		// Perform an assertion, but only if there are expected or unexpected deprecated calls or wrongdoings
+		if ( ! empty( $this->expected_deprecated ) ||
+			! empty( $this->expected_doing_it_wrong ) ||
+			! empty( $this->caught_deprecated ) ||
+			! empty( $this->caught_doing_it_wrong ) ) {
+			$this->assertEmpty( $errors, implode( "\n", $errors ) );
 		}
 	}
 
@@ -438,6 +464,17 @@ function assertNotWPError( $actual, $message = '' ) {
 		$this->assertNotInstanceOf( 'WP_Error', $actual, $message );
 	}
 
+	function assertIXRError( $actual, $message = '' ) {
+		$this->assertInstanceOf( 'IXR_Error', $actual, $message );
+	}
+
+	function assertNotIXRError( $actual, $message = '' ) {
+		if ( $actual instanceof IXR_Error && '' === $message ) {
+			$message = $actual->message;
+		}
+		$this->assertNotInstanceOf( 'IXR_Error', $actual, $message );
+	}
+
 	function assertEqualFields( $object, $fields ) {
 		foreach( $fields as $field_name => $field_value ) {
 			if ( $object->$field_name != $field_value ) {
@@ -540,6 +577,17 @@ function go_to( $url ) {
 	protected function checkRequirements() {
 		parent::checkRequirements();
 
+		$annotations = $this->getAnnotations();
+
+		if ( ! empty( $annotations['group'] ) ) {
+			if ( in_array( 'ms-required', $annotations['group'], true ) ) {
+				$this->skipWithoutMultisite();
+			}
+			if ( in_array( 'ms-excluded', $annotations['group'], true ) ) {
+				$this->skipWithMultisite();
+			}
+		}
+
 		// Core tests no longer check against open Trac tickets, but others using WP_UnitTestCase may do so.
 		if ( defined( 'WP_RUN_CORE_TESTS' ) && WP_RUN_CORE_TESTS ) {
 			return;
@@ -551,10 +599,6 @@ protected function checkRequirements() {
 		foreach ( $tickets as $ticket ) {
 			if ( is_numeric( $ticket ) ) {
 				$this->knownWPBug( $ticket );
-			} elseif ( 'UT' == substr( $ticket, 0, 2 ) ) {
-				$ticket = substr( $ticket, 2 );
-				if ( $ticket && is_numeric( $ticket ) )
-					$this->knownUTBug( $ticket );
 			} elseif ( 'Plugin' == substr( $ticket, 0, 6 ) ) {
 				$ticket = substr( $ticket, 6 );
 				if ( $ticket && is_numeric( $ticket ) )
@@ -574,13 +618,10 @@ function knownWPBug( $ticket_id ) {
 	}
 
 	/**
-	 * Skips the current test if there is an open unit tests ticket with id $ticket_id
+	 * @deprecated No longer used since the unit test Trac was merged into Core's.
 	 */
 	function knownUTBug( $ticket_id ) {
-		if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( 'UT' . $ticket_id, self::$forced_tickets ) )
-			return;
-		if ( ! TracTickets::isTracTicketClosed( 'https://unit-tests.trac.wordpress.org', $ticket_id ) )
-			$this->markTestSkipped( sprintf( 'Unit Tests Ticket #%d is not fixed', $ticket_id ) );
+		return;
 	}
 
 	/**
@@ -627,7 +668,7 @@ function temp_filename() {
 	/**
 	 * Check each of the WP_Query is_* functions/properties against expected boolean value.
 	 *
-	 * Any properties that are listed by name as parameters will be expected to be true; any others are
+	 * Any properties that are listed by name as parameters will be expected to be true; all others are
 	 * expected to be false. For example, assertQueryTrue('is_single', 'is_feed') means is_single()
 	 * and is_feed() must be true and everything else must be false to pass.
 	 *
@@ -668,32 +709,29 @@ function assertQueryTrue(/* ... */) {
 		$true = func_get_args();
 
 		foreach ( $true as $true_thing ) {
-			$this->assertContains( $true_thing, $all, "{$true_thing}() is not handled by assertQueryTrue()." );
+			$this->assertContains( $true_thing, $all, "Unknown conditional: {$true_thing}." );
 		}
 
 		$passed = true;
-		$not_false = $not_true = array(); // properties that were not set to expected values
+		$message = '';
 
 		foreach ( $all as $query_thing ) {
 			$result = is_callable( $query_thing ) ? call_user_func( $query_thing ) : $wp_query->$query_thing;
 
 			if ( in_array( $query_thing, $true ) ) {
 				if ( ! $result ) {
-					array_push( $not_true, $query_thing );
+					$message .= $query_thing . ' is false but is expected to be true. ' . PHP_EOL;
 					$passed = false;
 				}
 			} else if ( $result ) {
-				array_push( $not_false, $query_thing );
+				$message .= $query_thing . ' is true but is expected to be false. ' . PHP_EOL;
 				$passed = false;
 			}
 		}
 
-		$message = '';
-		if ( count($not_true) )
-			$message .= implode( $not_true, ', ' ) . ' is expected to be true. ';
-		if ( count($not_false) )
-			$message .= implode( $not_false, ', ' ) . ' is expected to be false.';
-		$this->assertTrue( $passed, $message );
+		if ( ! $passed ) {
+			$this->fail( $message );
+		}
 	}
 
 	function unlink( $file ) {
diff --git a/tests/phpunit/includes/utils.php b/tests/phpunit/includes/utils.php
index 66c8756..96d9bad 100644
--- a/tests/phpunit/includes/utils.php
+++ b/tests/phpunit/includes/utils.php
@@ -157,7 +157,7 @@ function __construct( $in ) {
 	}
 
 	function parse($in) {
-		$parse = xml_parse($this->xml, $in, sizeof($in));
+		$parse = xml_parse($this->xml, $in, true);
 		if (!$parse) {
 			trigger_error(sprintf("XML error: %s at line %d",
 			xml_error_string(xml_get_error_code($this->xml)),
diff --git a/tests/phpunit/multisite.xml b/tests/phpunit/multisite.xml
index f30452f..1aac733 100644
--- a/tests/phpunit/multisite.xml
+++ b/tests/phpunit/multisite.xml
@@ -2,6 +2,7 @@
 		bootstrap="includes/bootstrap.php"
         backupGlobals="false"
         colors="true"
+        beStrictAboutTestsThatDoNotTestAnything="true"
         >
     <php>
         <const name="WP_TESTS_MULTISITE" value="1" />
@@ -24,6 +25,7 @@
         <exclude>
             <group>ajax</group>
             <group>ms-files</group>
+            <group>ms-excluded</group>
             <group>external-http</group>
             <group>oembed-headers</group>
         </exclude>
diff --git a/tests/phpunit/tests/adminbar.php b/tests/phpunit/tests/adminbar.php
index c9f0873..b6ec315 100644
--- a/tests/phpunit/tests/adminbar.php
+++ b/tests/phpunit/tests/adminbar.php
@@ -72,12 +72,9 @@ function test_merging_existing_meta_values() {
 
 	/**
 	 * @ticket 25162
+	 * @group ms-excluded
 	 */
 	public function test_admin_bar_contains_correct_links_for_users_with_no_role() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run in multisite' );
-		}
-
 		$this->assertFalse( user_can( self::$no_role_id, 'read' ) );
 
 		wp_set_current_user( self::$no_role_id );
@@ -100,12 +97,9 @@ public function test_admin_bar_contains_correct_links_for_users_with_no_role() {
 
 	/**
 	 * @ticket 25162
+	 * @group ms-excluded
 	 */
 	public function test_admin_bar_contains_correct_links_for_users_with_role() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run in multisite' );
-		}
-
 		$this->assertTrue( user_can( self::$editor_id, 'read' ) );
 
 		wp_set_current_user( self::$editor_id );
@@ -131,12 +125,9 @@ public function test_admin_bar_contains_correct_links_for_users_with_role() {
 	/**
 	 * @ticket 25162
 	 * @group multisite
+	 * @group ms-required
 	 */
 	public function test_admin_bar_contains_correct_links_for_users_with_no_role_on_blog() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		$blog_id = self::factory()->blog->create( array(
 			'user_id' => self::$admin_id,
 		) );
@@ -181,12 +172,9 @@ public function test_admin_bar_contains_correct_links_for_users_with_no_role_on_
 	/**
 	 * @ticket 25162
 	 * @group multisite
+	 * @group ms-required
 	 */
 	public function test_admin_bar_contains_correct_links_for_users_with_no_role_on_network() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		$this->assertTrue( user_can( self::$admin_id, 'read' ) );
 		$this->assertFalse( user_can( self::$no_role_id, 'read' ) );
 
@@ -425,12 +413,9 @@ public function test_admin_bar_has_no_archives_link_for_pages() {
 
 	/**
 	 * @ticket 37949
+	 * @group ms-excluded
 	 */
 	public function test_admin_bar_contains_correct_about_link_for_users_with_role() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run in multisite' );
-		}
-
 		wp_set_current_user( self::$editor_id );
 
 		$wp_admin_bar = $this->get_standard_admin_bar();
@@ -445,12 +430,9 @@ public function test_admin_bar_contains_correct_about_link_for_users_with_role()
 
 	/**
 	 * @ticket 37949
+	 * @group ms-excluded
 	 */
 	public function test_admin_bar_contains_correct_about_link_for_users_with_no_role() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run in multisite' );
-		}
-
 		wp_set_current_user( self::$no_role_id );
 
 		$wp_admin_bar = $this->get_standard_admin_bar();
@@ -467,12 +449,9 @@ public function test_admin_bar_contains_correct_about_link_for_users_with_no_rol
 	/**
 	 * @ticket 37949
 	 * @group multisite
+	 * @group ms-required
 	 */
 	public function test_admin_bar_contains_correct_about_link_for_users_with_no_role_in_multisite() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		// User is not a member of a site.
 		remove_user_from_blog( self::$no_role_id, get_current_blog_id() );
 
@@ -586,12 +565,9 @@ public function test_customize_link() {
 
 	/**
 	 * @ticket 39082
+	 * @group ms-required
 	 */
 	public function test_my_sites_network_menu_for_regular_user() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		wp_set_current_user( self::$editor_id );
 
 		$wp_admin_bar = $this->get_standard_admin_bar();
@@ -604,12 +580,9 @@ public function test_my_sites_network_menu_for_regular_user() {
 
 	/**
 	 * @ticket 39082
+	 * @group ms-required
 	 */
 	public function test_my_sites_network_menu_for_super_admin() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		wp_set_current_user( self::$editor_id );
 
 		grant_super_admin( self::$editor_id );
@@ -624,14 +597,11 @@ public function test_my_sites_network_menu_for_super_admin() {
 
 	/**
 	 * @ticket 39082
+	 * @group ms-required
 	 */
 	public function test_my_sites_network_menu_for_regular_user_with_network_caps() {
 		global $current_user;
 
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		$network_user_caps = array( 'manage_network', 'manage_network_themes', 'manage_network_plugins' );
 
 		wp_set_current_user( self::$editor_id );
diff --git a/tests/phpunit/tests/ajax/Compression.php b/tests/phpunit/tests/ajax/Compression.php
index d82e0c5..1e65d71 100644
--- a/tests/phpunit/tests/ajax/Compression.php
+++ b/tests/phpunit/tests/ajax/Compression.php
@@ -57,7 +57,7 @@ public function test_text() {
 	public function test_gzdeflate() {
 
 		if ( !function_exists( 'gzdeflate' ) ) {
-			$this->markTestSkipped( 'gzdeflate function not available' );
+			$this->fail( 'gzdeflate function not available' );
 		}
 
 		// Become an administrator
@@ -84,7 +84,7 @@ public function test_gzdeflate() {
 	public function test_gzencode() {
 
 		if ( !function_exists('gzencode') ) {
-			$this->markTestSkipped( 'gzencode function not available' );
+			$this->fail( 'gzencode function not available' );
 		}
 
 		// Become an administrator
diff --git a/tests/phpunit/tests/comment/checkComment.php b/tests/phpunit/tests/comment/checkComment.php
index 0bedc4a..b33ccd7 100644
--- a/tests/phpunit/tests/comment/checkComment.php
+++ b/tests/phpunit/tests/comment/checkComment.php
@@ -83,6 +83,8 @@ public function test_should_return_false_when_content_matches_moderation_key() {
 	}
 
 	public function test_should_return_true_when_content_does_not_match_moderation_keys() {
+		update_option( 'comment_whitelist', 0 );
+
 		$author       = 'WendytheBuilder';
 		$author_email = 'wendy@example.com';
 		$author_url   = 'http://example.com';
@@ -93,7 +95,7 @@ public function test_should_return_true_when_content_does_not_match_moderation_k
 
 		update_option( 'moderation_keys',"foo\nbar" );
 		$results = check_comment( $author, $author_email, $author_url, $comment, $author_ip, $user_agent, $comment_type );
-		$this->assertFalse( $results );
+		$this->assertTrue( $results );
 	}
 
 	public function test_should_return_false_when_link_count_exceeds_comment_max_length_setting() {
diff --git a/tests/phpunit/tests/customize/manager.php b/tests/phpunit/tests/customize/manager.php
index 16d1e4a..c141e38 100644
--- a/tests/phpunit/tests/customize/manager.php
+++ b/tests/phpunit/tests/customize/manager.php
@@ -2027,15 +2027,6 @@ function filter_customize_dynamic_setting_class_for_test_dynamic_settings( $sett
 	}
 
 	/**
-	 * Test is_ios() method.
-	 *
-	 * @see WP_Customize_Manager::is_ios()
-	 */
-	function test_is_ios() {
-		$this->markTestSkipped( 'WP_Customize_Manager::is_ios() cannot be tested because it uses wp_is_mobile() which contains a static var.' );
-	}
-
-	/**
 	 * Test get_document_title_template() method.
 	 *
 	 * @see WP_Customize_Manager::get_document_title_template()
diff --git a/tests/phpunit/tests/customize/setting.php b/tests/phpunit/tests/customize/setting.php
index 3ee1a8c..2450ec2 100644
--- a/tests/phpunit/tests/customize/setting.php
+++ b/tests/phpunit/tests/customize/setting.php
@@ -561,12 +561,9 @@ function test_is_current_blog_previewed() {
 	 *
 	 * @ticket 31428
 	 * @group multisite
+	 * @group ms-required
 	 */
 	function test_previewing_with_switch_to_blog() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Cannot test WP_Customize_Setting::is_current_blog_previewed() with switch_to_blog() if not on multisite.' );
-		}
-
 		wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
 		$type = 'option';
 		$name = 'blogdescription';
diff --git a/tests/phpunit/tests/db.php b/tests/phpunit/tests/db.php
index 9595d3d..ab4186c 100644
--- a/tests/phpunit/tests/db.php
+++ b/tests/phpunit/tests/db.php
@@ -507,7 +507,7 @@ function test_mysqli_flush_sync() {
 
 		if ( count( $wpdb->get_results( 'SHOW CREATE PROCEDURE `test_mysqli_flush_sync_procedure`' ) ) < 1 ) {
 			$wpdb->suppress_errors( $suppress );
-			$this->markTestSkipped( 'procedure could not be created (missing privileges?)' );
+			$this->fail( 'procedure could not be created (missing privileges?)' );
 		}
 
 		$post_id = self::factory()->post->create();
diff --git a/tests/phpunit/tests/external-http/basic.php b/tests/phpunit/tests/external-http/basic.php
index e84ab6a..8799746 100644
--- a/tests/phpunit/tests/external-http/basic.php
+++ b/tests/phpunit/tests/external-http/basic.php
@@ -14,7 +14,7 @@ function test_readme() {
 
 		$response = wp_remote_get( 'https://secure.php.net/supported-versions.php' );
 		if ( 200 != wp_remote_retrieve_response_code( $response ) ) {
-			$this->markTestSkipped( 'Could not contact PHP.net to check versions.' );
+			$this->fail( 'Could not contact PHP.net to check versions.' );
 		}
 		$php = wp_remote_retrieve_body( $response );
 
@@ -26,7 +26,7 @@ function test_readme() {
 
 		$response = wp_remote_get( "https://dev.mysql.com/doc/relnotes/mysql/{$matches[1]}/en/" );
 		if ( 200 != wp_remote_retrieve_response_code( $response ) ) {
-			$this->markTestSkipped( 'Could not contact dev.MySQL.com to check versions.' );
+			$this->fail( 'Could not contact dev.MySQL.com to check versions.' );
 		}
 		$mysql = wp_remote_retrieve_body( $response );
 
diff --git a/tests/phpunit/tests/feed/rss2.php b/tests/phpunit/tests/feed/rss2.php
index d48bc88..f0d7919 100644
--- a/tests/phpunit/tests/feed/rss2.php
+++ b/tests/phpunit/tests/feed/rss2.php
@@ -162,9 +162,6 @@ function test_channel_pubdate_element_translated() {
 		$this->assertNotContains( 'Tue_Translated', $pubdate[0]['content'] );
 	}
 
-	/**
-	 * @ticket UT32
-	 */
 	function test_item_elements() {
 		$this->go_to( '/?feed=rss2' );
 		$feed = $this->do_rss2();
diff --git a/tests/phpunit/tests/functions.php b/tests/phpunit/tests/functions.php
index 47704f8..252d890 100644
--- a/tests/phpunit/tests/functions.php
+++ b/tests/phpunit/tests/functions.php
@@ -61,9 +61,6 @@ function test_bool_from_yn() {
 	}
 
 	function test_path_is_absolute() {
-		if ( !is_callable('path_is_absolute') )
-			$this->markTestSkipped();
-
 		$absolute_paths = array(
 			'/',
 			'/foo/',
@@ -81,9 +78,6 @@ function test_path_is_absolute() {
 	}
 
 	function test_path_is_not_absolute() {
-		if ( !is_callable('path_is_absolute') )
-			$this->markTestSkipped();
-
 		$relative_paths = array(
 			'',
 			'.',
@@ -941,16 +935,13 @@ function test_wp_check_filetype_and_ext( $file, $filename, $expected ) {
 
 	/**
 	 * @ticket 39550
+	 * @group ms-excluded
 	 */
 	function test_wp_check_filetype_and_ext_with_filtered_svg() {
 		if ( ! extension_loaded( 'fileinfo' ) ) {
 			$this->markTestSkipped( 'The fileinfo PHP extension is not loaded.' );
 		}
 
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run in multisite' );
-		}
-
 		$file = DIR_TESTDATA . '/uploads/video-play.svg';
 		$filename = 'video-play.svg';
 
@@ -969,16 +960,13 @@ function test_wp_check_filetype_and_ext_with_filtered_svg() {
 
 	/**
 	 * @ticket 39550
+	 * @group ms-excluded
 	 */
 	function test_wp_check_filetype_and_ext_with_filtered_woff() {
 		if ( ! extension_loaded( 'fileinfo' ) ) {
 			$this->markTestSkipped( 'The fileinfo PHP extension is not loaded.' );
 		}
 
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run in multisite' );
-		}
-
 		$file = DIR_TESTDATA . '/uploads/dashicons.woff';
 		$filename = 'dashicons.woff';
 
diff --git a/tests/phpunit/tests/functions/deprecated.php b/tests/phpunit/tests/functions/deprecated.php
index 542ea39..9735fd8 100644
--- a/tests/phpunit/tests/functions/deprecated.php
+++ b/tests/phpunit/tests/functions/deprecated.php
@@ -140,7 +140,7 @@ protected function was_deprecated( $type, $name ) {
 	 */
 	public function test_wp_save_image_file_deprecated_with_gd_resource() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		// Call wp_save_image_file
 		include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
@@ -161,7 +161,7 @@ public function test_wp_save_image_file_deprecated_with_gd_resource() {
 	 */
 	public function test_wp_save_image_file_not_deprecated_with_wp_image_editor() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		// Call wp_save_image_file
 		include_once( ABSPATH . 'wp-admin/includes/image-edit.php' );
diff --git a/tests/phpunit/tests/general/template.php b/tests/phpunit/tests/general/template.php
index 16007cf..4d47a4d 100644
--- a/tests/phpunit/tests/general/template.php
+++ b/tests/phpunit/tests/general/template.php
@@ -72,12 +72,9 @@ function test_has_site_icon() {
 	/**
  	 * @group site_icon
 	 * @group multisite
+	 * @group ms-required
 	 */
 	function test_has_site_icon_returns_true_when_called_for_other_site_with_site_icon_set() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite.' );
-		}
-
 		$blog_id = $this->factory->blog->create();
 		switch_to_blog( $blog_id );
 		$this->_set_site_icon();
@@ -89,12 +86,9 @@ function test_has_site_icon_returns_true_when_called_for_other_site_with_site_ic
 	/**
 	 * @group site_icon
 	 * @group multisite
+	 * @group ms-required
 	 */
 	function test_has_site_icon_returns_false_when_called_for_other_site_without_site_icon_set() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite.' );
-		}
-
 		$blog_id = $this->factory->blog->create();
 
 		$this->assertFalse( has_site_icon( $blog_id ) );
@@ -264,12 +258,9 @@ function test_has_custom_logo() {
 	/**
 	 * @group custom_logo
 	 * @group multisite
+	 * @group ms-required
 	 */
 	function test_has_custom_logo_returns_true_when_called_for_other_site_with_custom_logo_set() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite.' );
-		}
-
 		$blog_id = $this->factory->blog->create();
 		switch_to_blog( $blog_id );
 		$this->_set_custom_logo();
@@ -281,12 +272,9 @@ function test_has_custom_logo_returns_true_when_called_for_other_site_with_custo
 	/**
 	 * @group custom_logo
 	 * @group multisite
+	 * @group ms-required
 	 */
 	function test_has_custom_logo_returns_false_when_called_for_other_site_without_custom_logo_set() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite.' );
-		}
-
 		$blog_id = $this->factory->blog->create();
 
 		$this->assertFalse( has_custom_logo( $blog_id ) );
@@ -312,12 +300,9 @@ function test_get_custom_logo() {
 	/**
 	 * @group custom_logo
 	 * @group multisite
+	 * @group ms-required
 	 */
 	function test_get_custom_logo_returns_logo_when_called_for_other_site_with_custom_logo_set() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite.' );
-		}
-
 		$blog_id = $this->factory->blog->create();
 		switch_to_blog( $blog_id );
 
@@ -534,12 +519,9 @@ function test_get_the_modified_time_with_post_id() {
 
 	/**
 	 * @ticket 38253
+	 * @group ms-required
 	 */
 	function test_get_site_icon_url_preserves_switched_state() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite.' );
-		}
-
 		$blog_id = $this->factory->blog->create();
 		switch_to_blog( $blog_id );
 
@@ -556,12 +538,9 @@ function test_get_site_icon_url_preserves_switched_state() {
 
 	/**
 	 * @ticket 38253
+	 * @group ms-required
 	 */
 	function test_has_custom_logo_preserves_switched_state() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite.' );
-		}
-
 		$blog_id = $this->factory->blog->create();
 		switch_to_blog( $blog_id );
 
@@ -578,12 +557,9 @@ function test_has_custom_logo_preserves_switched_state() {
 
 	/**
 	 * @ticket 38253
+	 * @group ms-required
 	 */
 	function test_get_custom_logo_preserves_switched_state() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite.' );
-		}
-
 		$blog_id = $this->factory->blog->create();
 		switch_to_blog( $blog_id );
 
diff --git a/tests/phpunit/tests/http/base.php b/tests/phpunit/tests/http/base.php
index a546fb7..a879278 100644
--- a/tests/phpunit/tests/http/base.php
+++ b/tests/phpunit/tests/http/base.php
@@ -384,7 +384,7 @@ function test_cookie_handling() {
 	 */
 	function test_ssl() {
 		if ( ! wp_http_supports( array( 'ssl' ) ) )
-			$this->markTestSkipped( 'This install of PHP does not support SSL' );
+			$this->fail( 'This install of PHP does not support SSL' );
 
 		$res = wp_remote_get( 'https://wordpress.org/' );
 		$this->assertNotWPError( $res );
diff --git a/tests/phpunit/tests/image/editor_gd.php b/tests/phpunit/tests/image/editor_gd.php
index ecb599e..cdfc207 100644
--- a/tests/phpunit/tests/image/editor_gd.php
+++ b/tests/phpunit/tests/image/editor_gd.php
@@ -468,7 +468,7 @@ public function test_flip() {
 	 */
 	public function test_image_preserves_alpha_on_resize() {
 		if ( ! ( imagetypes() & IMG_PNG ) ) {
-			$this->markTestSkipped( 'This test requires PHP to be compiled with PNG support.' );
+			$this->fail( 'This test requires PHP to be compiled with PNG support.' );
 		}
 
 		$file = DIR_TESTDATA . '/images/transparent.png';
@@ -495,7 +495,7 @@ public function test_image_preserves_alpha_on_resize() {
 	 */
 	public function test_image_preserves_alpha() {
 		if ( ! ( imagetypes() & IMG_PNG ) ) {
-			$this->markTestSkipped( 'This test requires PHP to be compiled with PNG support.' );
+			$this->fail( 'This test requires PHP to be compiled with PNG support.' );
 		}
 
 		$file = DIR_TESTDATA . '/images/transparent.png';
@@ -521,7 +521,7 @@ public function test_image_preserves_alpha() {
 	 */
 	public function test_image_preserves_alpha_on_rotate() {
 		if ( ! ( imagetypes() & IMG_PNG ) ) {
-			$this->markTestSkipped( 'This test requires PHP to be compiled with PNG support.' );
+			$this->fail( 'This test requires PHP to be compiled with PNG support.' );
 		}
 
 		$file = DIR_TESTDATA . '/images/transparent.png';
diff --git a/tests/phpunit/tests/image/functions.php b/tests/phpunit/tests/image/functions.php
index 7ed2f0b..27bdbf3 100644
--- a/tests/phpunit/tests/image/functions.php
+++ b/tests/phpunit/tests/image/functions.php
@@ -289,7 +289,7 @@ public function test_load_directory() {
 
 	public function test_wp_crop_image_file() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		$file = wp_crop_image( DIR_TESTDATA . '/images/canola.jpg',
 							  0, 0, 100, 100, 100, 100 );
@@ -305,7 +305,7 @@ public function test_wp_crop_image_file() {
 
 	public function test_wp_crop_image_url() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		if ( ! extension_loaded( 'openssl' ) ) {
 			$this->markTestSkipped( 'Tests_Image_Functions::test_wp_crop_image_url() requires openssl.' );
diff --git a/tests/phpunit/tests/image/intermediate_size.php b/tests/phpunit/tests/image/intermediate_size.php
index 09419a2..3fcdf8c 100644
--- a/tests/phpunit/tests/image/intermediate_size.php
+++ b/tests/phpunit/tests/image/intermediate_size.php
@@ -30,7 +30,7 @@ function test_make_intermediate_size_no_size() {
 
 	function test_make_intermediate_size_width() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		$image = image_make_intermediate_size( DIR_TESTDATA . '/images/a2-small.jpg', 100, 0, false );
 
@@ -39,7 +39,7 @@ function test_make_intermediate_size_width() {
 
 	function test_make_intermediate_size_height() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		$image = image_make_intermediate_size( DIR_TESTDATA . '/images/a2-small.jpg', 0, 75, false );
 
@@ -48,7 +48,7 @@ function test_make_intermediate_size_height() {
 
 	function test_make_intermediate_size_successful() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		$image = image_make_intermediate_size( DIR_TESTDATA . '/images/a2-small.jpg', 100, 75, true );
 
diff --git a/tests/phpunit/tests/image/meta.php b/tests/phpunit/tests/image/meta.php
index 30613d4..f132d90 100644
--- a/tests/phpunit/tests/image/meta.php
+++ b/tests/phpunit/tests/image/meta.php
@@ -11,8 +11,6 @@ function setUp() {
 			$this->markTestSkipped( 'The gd PHP extension is not loaded.' );
 		if ( ! extension_loaded( 'exif' ) )
 			$this->markTestSkipped( 'The exif PHP extension is not loaded.' );
-		if ( ! is_callable( 'wp_read_image_metadata' ) )
-			$this->markTestSkipped( 'wp_read_image_metadata() is not callable.' );
 		parent::setUp();
 	}
 
diff --git a/tests/phpunit/tests/image/resize.php b/tests/phpunit/tests/image/resize.php
index 807b51c..a775eb5 100644
--- a/tests/phpunit/tests/image/resize.php
+++ b/tests/phpunit/tests/image/resize.php
@@ -42,8 +42,7 @@ function test_resize_png() {
 		$image = $this->resize_helper( DIR_TESTDATA.'/images/test-image.png', 25, 25 );
 
 		if ( ! is_string( $image ) ) {  // WP_Error, stop GLib-GObject-CRITICAL assertion
-			$this->markTestSkipped( sprintf( 'No PNG support in the editor engine %s on this system', $this->editor_engine ) );
-			return;
+			$this->fail( sprintf( 'No PNG support in the editor engine %s on this system', $this->editor_engine ) );
 		}
 
 		$this->assertEquals( 'test-image-25x25.png', basename( $image ) );
@@ -59,8 +58,7 @@ function test_resize_gif() {
 		$image = $this->resize_helper( DIR_TESTDATA.'/images/test-image.gif', 25, 25 );
 
 		if ( ! is_string( $image ) ) {  // WP_Error, stop GLib-GObject-CRITICAL assertion
-			$this->markTestSkipped( sprintf( 'No GIF support in the editor engine %s on this system', $this->editor_engine ) );
-			return;
+			$this->fail( sprintf( 'No GIF support in the editor engine %s on this system', $this->editor_engine ) );
 		}
 
 		$this->assertEquals( 'test-image-25x25.gif', basename( $image ) );
diff --git a/tests/phpunit/tests/image/size.php b/tests/phpunit/tests/image/size.php
index 497946c..c27bbcf 100644
--- a/tests/phpunit/tests/image/size.php
+++ b/tests/phpunit/tests/image/size.php
@@ -8,9 +8,6 @@
 class Tests_Image_Size extends WP_UnitTestCase {
 
 	function test_constrain_dims_zero() {
-		if (!is_callable('wp_constrain_dimensions'))
-			$this->markTestSkipped('wp_constrain_dimensions() is not callable.');
-
 		// no constraint - should have no effect
 		$out = wp_constrain_dimensions(640, 480, 0, 0);
 		$this->assertSame( array( 640, 480 ), $out );
@@ -26,9 +23,6 @@ function test_constrain_dims_zero() {
 	}
 
 	function test_constrain_dims_smaller() {
-		if (!is_callable('wp_constrain_dimensions'))
-			$this->markTestSkipped('wp_constrain_dimensions() is not callable.');
-
 		// image size is smaller than the constraint - no effect
 		$out = wp_constrain_dimensions(500, 600, 1024, 768);
 		$this->assertSame( array( 500, 600 ), $out );
@@ -41,9 +35,6 @@ function test_constrain_dims_smaller() {
 	}
 
 	function test_constrain_dims_equal() {
-		if (!is_callable('wp_constrain_dimensions'))
-			$this->markTestSkipped('wp_constrain_dimensions() is not callable.');
-
 		// image size is equal to the constraint - no effect
 		$out = wp_constrain_dimensions(1024, 768, 1024, 768);
 		$this->assertSame( array( 1024, 768 ), $out );
@@ -56,9 +47,6 @@ function test_constrain_dims_equal() {
 	}
 
 	function test_constrain_dims_larger() {
-		if (!is_callable('wp_constrain_dimensions'))
-			$this->markTestSkipped('wp_constrain_dimensions() is not callable.');
-
 		// image size is larger than the constraint - result should be constrained
 		$out = wp_constrain_dimensions(1024, 768, 500, 600);
 		$this->assertSame( array( 500, 375 ), $out );
@@ -81,9 +69,6 @@ function test_constrain_dims_larger() {
 	}
 
 	function test_constrain_dims_boundary() {
-		if (!is_callable('wp_constrain_dimensions'))
-			$this->markTestSkipped('wp_constrain_dimensions() is not callable.');
-
 		// one dimension is larger than the constraint, one smaller - result should be constrained
 		$out = wp_constrain_dimensions(1024, 768, 500, 800);
 		$this->assertSame( array( 500, 375 ), $out );
diff --git a/tests/phpunit/tests/import/import.php b/tests/phpunit/tests/import/import.php
index fa0bcbb..9303444 100644
--- a/tests/phpunit/tests/import/import.php
+++ b/tests/phpunit/tests/import/import.php
@@ -18,7 +18,7 @@ function setUp() {
 		add_filter( 'import_allow_create_users', '__return_true' );
 
 		if ( ! file_exists( DIR_TESTDATA . '/plugins/wordpress-importer/wordpress-importer.php' ) ) {
-			$this->markTestSkipped( 'WordPress Importer plugin is not installed.' );
+			$this->fail( 'WordPress Importer plugin is not installed.' );
 		}
 
 		require_once DIR_TESTDATA . '/plugins/wordpress-importer/wordpress-importer.php';
diff --git a/tests/phpunit/tests/import/parser.php b/tests/phpunit/tests/import/parser.php
index 3e2ee20..890fd0a 100644
--- a/tests/phpunit/tests/import/parser.php
+++ b/tests/phpunit/tests/import/parser.php
@@ -16,7 +16,7 @@ function setUp() {
 			define( 'WP_LOAD_IMPORTERS', true );
 
 		if ( ! file_exists( DIR_TESTDATA . '/plugins/wordpress-importer/wordpress-importer.php' ) ) {
-			$this->markTestSkipped( 'WordPress Importer plugin is not installed.' );
+			$this->fail( 'WordPress Importer plugin is not installed.' );
 		}
 
 		require_once DIR_TESTDATA . '/plugins/wordpress-importer/wordpress-importer.php';
diff --git a/tests/phpunit/tests/import/postmeta.php b/tests/phpunit/tests/import/postmeta.php
index 0fd8dae..bf468a1 100644
--- a/tests/phpunit/tests/import/postmeta.php
+++ b/tests/phpunit/tests/import/postmeta.php
@@ -16,7 +16,7 @@ function setUp() {
 			define( 'WP_LOAD_IMPORTERS', true );
 
 		if ( ! file_exists( DIR_TESTDATA . '/plugins/wordpress-importer/wordpress-importer.php' ) ) {
-			$this->markTestSkipped( 'WordPress Importer plugin is not installed.' );
+			$this->fail( 'WordPress Importer plugin is not installed.' );
 		}
 
 		require_once DIR_TESTDATA . '/plugins/wordpress-importer/wordpress-importer.php';
diff --git a/tests/phpunit/tests/l10n/getLocale.php b/tests/phpunit/tests/l10n/getLocale.php
index 64b9900..ac9e429 100644
--- a/tests/phpunit/tests/l10n/getLocale.php
+++ b/tests/phpunit/tests/l10n/getLocale.php
@@ -17,11 +17,10 @@ public function test_should_respect_locale_global() {
 		$this->assertSame( 'foo', $found );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_local_option_should_take_precedence_on_multisite() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' requires Multisite' );
-		}
-
 		global $locale;
 		$old_locale = $locale;
 		$locale = null;
@@ -35,6 +34,9 @@ public function test_local_option_should_take_precedence_on_multisite() {
 		$this->assertSame( 'en_GB', $found );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_network_option_should_be_fallback_on_multisite() {
 		if ( ! is_multisite() ) {
 			$this->markTestSkipped( __METHOD__ . ' requires Multisite' );
@@ -52,6 +54,9 @@ public function test_network_option_should_be_fallback_on_multisite() {
 		$this->assertSame( 'es_ES', $found );
 	}
 
+	/**
+	 * @group ms-excluded
+	 */
 	public function test_option_should_be_respected_on_nonmultisite() {
 		if ( is_multisite() ) {
 			$this->markTestSkipped( __METHOD__ . ' does not apply to Multisite' );
diff --git a/tests/phpunit/tests/l10n/getUserLocale.php b/tests/phpunit/tests/l10n/getUserLocale.php
index c556426..d83b3e9 100644
--- a/tests/phpunit/tests/l10n/getUserLocale.php
+++ b/tests/phpunit/tests/l10n/getUserLocale.php
@@ -66,6 +66,9 @@ public function test_site_locale_is_not_affected_on_frontend() {
 		$this->assertSame( 'en_US', get_locale() );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_user_locale_is_same_across_network() {
 		if ( ! is_multisite() ) {
 			$this->markTestSkipped( __METHOD__ . ' requires multisite' );
diff --git a/tests/phpunit/tests/link/getDashboardUrl.php b/tests/phpunit/tests/link/getDashboardUrl.php
index 4a6be8b..8557be1 100644
--- a/tests/phpunit/tests/link/getDashboardUrl.php
+++ b/tests/phpunit/tests/link/getDashboardUrl.php
@@ -38,12 +38,9 @@ public function test_get_dashboard_url_for_user_with_no_sites() {
 
 	/**
 	 * @ticket 39065
+	 * @group ms-required
 	 */
 	public function test_get_dashboard_url_for_network_administrator_with_no_sites() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite.' );
-		}
-
 		grant_super_admin( self::$user_id );
 
 		add_filter( 'get_blogs_of_user', '__return_empty_array' );
@@ -58,12 +55,9 @@ public function test_get_dashboard_url_for_network_administrator_with_no_sites()
 
 	/**
 	 * @ticket 39065
+	 * @group ms-required
 	 */
 	public function test_get_dashboard_url_for_administrator_of_different_site() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite.' );
-		}
-
 		$site_id = self::factory()->blog->create( array( 'user_id' => self::$user_id ) );
 
 		remove_user_from_blog( self::$user_id, get_current_blog_id() );
diff --git a/tests/phpunit/tests/multisite/network.php b/tests/phpunit/tests/multisite/network.php
index ffd976e..71e3ed8 100644
--- a/tests/phpunit/tests/multisite/network.php
+++ b/tests/phpunit/tests/multisite/network.php
@@ -376,6 +376,100 @@ function test_get_dashboard_blog() {
 		$dashboard_blog = get_dashboard_blog();
 		$this->assertEquals( $blog_id, $dashboard_blog->blog_id );
 	}
+
+	/**
+	 * @ticket 37528
+	 */
+	function test_wp_update_network_site_counts() {
+		update_network_option( null, 'blog_count', 40 );
+
+		$expected = get_sites( array(
+			'network_id' => get_current_network_id(),
+			'spam'       => 0,
+			'deleted'    => 0,
+			'archived'   => 0,
+			'count'      => true,
+		) );
+
+		wp_update_network_site_counts();
+
+		$result = get_blog_count();
+		$this->assertEquals( $expected, $result );
+	}
+
+	/**
+	 * @ticket 37528
+	 */
+	function test_wp_update_network_site_counts_on_different_network() {
+		update_network_option( self::$different_network_id, 'blog_count', 40 );
+
+		wp_update_network_site_counts( self::$different_network_id );
+
+		$result = get_blog_count( self::$different_network_id );
+		$this->assertEquals( 3, $result );
+	}
+
+	/**
+	 * @ticket 40349
+	 */
+	public function test_wp_update_network_user_counts() {
+		global $wpdb;
+
+		update_network_option( null, 'user_count', 40 );
+
+		$expected = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
+
+		wp_update_network_user_counts();
+
+		$result = get_user_count();
+		$this->assertEquals( $expected, $result );
+	}
+
+	/**
+	 * @ticket 40349
+	 */
+	public function test_wp_update_network_user_counts_on_different_network() {
+		global $wpdb;
+
+		update_network_option( self::$different_network_id, 'user_count', 40 );
+
+		$expected = $wpdb->get_var( "SELECT COUNT(ID) as c FROM $wpdb->users WHERE spam = '0' AND deleted = '0'" );
+
+		wp_update_network_user_counts( self::$different_network_id );
+
+		$result = get_user_count( self::$different_network_id );
+		$this->assertEquals( $expected, $result );
+	}
+
+	/**
+	 * @ticket 40386
+	 */
+	public function test_wp_update_network_counts() {
+		delete_network_option( null, 'site_count' );
+		delete_network_option( null, 'user_count' );
+
+		wp_update_network_counts();
+
+		$site_count = (int) get_blog_count();
+		$user_count = (int) get_user_count();
+
+		$this->assertTrue( $site_count > 0 && $user_count > 0 );
+	}
+
+	/**
+	 * @ticket 40386
+	 */
+	public function test_wp_update_network_counts_on_different_network() {
+		delete_network_option( self::$different_network_id, 'site_count' );
+		delete_network_option( self::$different_network_id, 'user_count' );
+
+		wp_update_network_counts( self::$different_network_id );
+
+		$site_count = (int) get_blog_count( self::$different_network_id );
+		$user_count = (int) get_user_count( self::$different_network_id );
+
+		$this->assertTrue( $site_count > 0 && $user_count > 0 );
+	}
 }
 
 endif;
diff --git a/tests/phpunit/tests/multisite/siteDetails.php b/tests/phpunit/tests/multisite/siteDetails.php
index c58360b..bce6aaea 100644
--- a/tests/phpunit/tests/multisite/siteDetails.php
+++ b/tests/phpunit/tests/multisite/siteDetails.php
@@ -144,6 +144,49 @@ public function test_site_details_cached_including_false_values() {
 
 		$this->assertNotFalse( $cached_details );
 	}
+
+	public function test_site_details_filter_with_blogname() {
+		add_filter( 'site_details', array( $this, '_filter_site_details_blogname' ) );
+		$site = get_site();
+		$blogname = $site->blogname;
+		remove_filter( 'site_details', array( $this, '_filter_site_details_blogname' ) );
+
+		$this->assertSame( 'Foo Bar', $blogname );
+	}
+
+	public function _filter_site_details_blogname( $details ) {
+		$details->blogname = 'Foo Bar';
+		return $details;
+	}
+
+	/**
+	 * @ticket 40458
+	 */
+	public function test_site_details_filter_with_custom_value_isetter() {
+		add_filter( 'site_details', array( $this, '_filter_site_details_custom_value' ) );
+		$site = get_site();
+		$custom_value_isset = isset( $site->custom_value );
+		remove_filter( 'site_details', array( $this, '_filter_site_details_custom_value' ) );
+
+		$this->assertTrue( $custom_value_isset );
+	}
+
+	/**
+	 * @ticket 40458
+	 */
+	public function test_site_details_filter_with_custom_value_getter() {
+		add_filter( 'site_details', array( $this, '_filter_site_details_custom_value' ) );
+		$site = get_site();
+		$custom_value = $site->custom_value;
+		remove_filter( 'site_details', array( $this, '_filter_site_details_custom_value' ) );
+
+		$this->assertSame( 'foo', $custom_value );
+	}
+
+	public function _filter_site_details_custom_value( $details ) {
+		$details->custom_value = 'foo';
+		return $details;
+	}
 }
 
 endif;
diff --git a/tests/phpunit/tests/oembed/controller.php b/tests/phpunit/tests/oembed/controller.php
index 3c9d051..de41b0d 100644
--- a/tests/phpunit/tests/oembed/controller.php
+++ b/tests/phpunit/tests/oembed/controller.php
@@ -253,12 +253,9 @@ function test_request_xml() {
 
 	/**
 	 * @group multisite
+	 * @group ms-required
 	 */
 	function test_request_ms_child_in_root_blog() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' is a multisite-only test.' );
-		}
-
 		$child = self::factory()->blog->create();
 		switch_to_blog( $child );
 
diff --git a/tests/phpunit/tests/oembed/template.php b/tests/phpunit/tests/oembed/template.php
index 48245d1..7c82529 100644
--- a/tests/phpunit/tests/oembed/template.php
+++ b/tests/phpunit/tests/oembed/template.php
@@ -303,7 +303,7 @@ function test_js_no_ampersands_in_compiled() {
 
 		$file = dirname( ABSPATH ) . '/build/' . WPINC . '/js/wp-embed.min.js';
 		if ( ! file_exists( $file ) ) {
-			$this->markTestSkipped( "This test is for the compiled wp-embed.min.js file." );
+			return;
 		}
 		$this->assertNotContains( '&', file_get_contents( $file ) );
 	}
diff --git a/tests/phpunit/tests/option/networkOption.php b/tests/phpunit/tests/option/networkOption.php
index 17e96bd..656fcab 100644
--- a/tests/phpunit/tests/option/networkOption.php
+++ b/tests/phpunit/tests/option/networkOption.php
@@ -11,11 +11,11 @@
  * @group multisite
  */
 class Tests_Option_NetworkOption extends WP_UnitTestCase {
-	function test_add_network_option_not_available_on_other_network() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test requires multisite' );
-		}
 
+	/**
+	 * @group ms-required
+	 */
+	function test_add_network_option_not_available_on_other_network() {
 		$id = self::factory()->network->create();
 		$option = __FUNCTION__;
 		$value = __FUNCTION__;
@@ -24,11 +24,10 @@ function test_add_network_option_not_available_on_other_network() {
 		$this->assertFalse( get_network_option( $id, $option, false ) );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_add_network_option_available_on_same_network() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test requires multisite' );
-		}
-
 		$id = self::factory()->network->create();
 		$option = __FUNCTION__;
 		$value = __FUNCTION__;
@@ -37,11 +36,10 @@ function test_add_network_option_available_on_same_network() {
 		$this->assertEquals( $value, get_network_option( $id, $option, false ) );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_delete_network_option_on_only_one_network() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test requires multisite' );
-		}
-
 		$id = self::factory()->network->create();
 		$option = __FUNCTION__;
 		$value = __FUNCTION__;
@@ -54,14 +52,11 @@ function test_delete_network_option_on_only_one_network() {
 
 	/**
 	 * @ticket 22846
+	 * @group ms-excluded
 	 */
 	public function test_add_network_option_is_not_stored_as_autoload_option() {
 		$key = __FUNCTION__;
 
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Does not apply when used in multisite.' );
-		}
-
 		add_network_option( null, $key, 'Not an autoload option' );
 
 		$options = wp_load_alloptions();
@@ -71,14 +66,11 @@ public function test_add_network_option_is_not_stored_as_autoload_option() {
 
 	/**
 	 * @ticket 22846
+	 * @group ms-excluded
 	 */
 	public function test_update_network_option_is_not_stored_as_autoload_option() {
 		$key = __FUNCTION__;
 
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Does not apply when used in multisite.' );
-		}
-
 		update_network_option( null, $key, 'Not an autoload option' );
 
 		$options = wp_load_alloptions();
diff --git a/tests/phpunit/tests/option/siteTransient.php b/tests/phpunit/tests/option/siteTransient.php
index 4a9f0e2..8a2965f 100644
--- a/tests/phpunit/tests/option/siteTransient.php
+++ b/tests/phpunit/tests/option/siteTransient.php
@@ -44,13 +44,11 @@ function test_serialized_data() {
 
 	/**
 	 * @ticket 22846
+	 * @group ms-excluded
 	 */
 	public function test_set_site_transient_is_not_stored_as_autoload_option() {
 		$key = 'not_autoloaded';
 
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Does not apply when used in multisite.' );
-		}
 		set_site_transient( $key, 'Not an autoload option' );
 
 		$options = wp_load_alloptions();
diff --git a/tests/phpunit/tests/post/attachments.php b/tests/phpunit/tests/post/attachments.php
index 3df4ce4..306192c 100644
--- a/tests/phpunit/tests/post/attachments.php
+++ b/tests/phpunit/tests/post/attachments.php
@@ -57,7 +57,7 @@ function test_insert_image_no_thumb() {
 
 	function test_insert_image_thumb_only() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		update_option( 'medium_size_w', 0 );
 		update_option( 'medium_size_h', 0 );
@@ -109,7 +109,7 @@ function test_insert_image_thumb_only() {
 
 	function test_insert_image_medium_sizes() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		update_option('medium_size_w', 400);
 		update_option('medium_size_h', 0);
@@ -167,7 +167,7 @@ function test_insert_image_medium_sizes() {
 
 	function test_insert_image_delete() {
 		if ( !function_exists( 'imagejpeg' ) )
-			$this->markTestSkipped( 'jpeg support unavailable' );
+			$this->fail( 'jpeg support unavailable' );
 
 		update_option('medium_size_w', 400);
 		update_option('medium_size_h', 0);
diff --git a/tests/phpunit/tests/rest-api/rest-posts-controller.php b/tests/phpunit/tests/rest-api/rest-posts-controller.php
index 3aa91e4..de9b0db 100644
--- a/tests/phpunit/tests/rest-api/rest-posts-controller.php
+++ b/tests/phpunit/tests/rest-api/rest-posts-controller.php
@@ -569,27 +569,6 @@ public function test_get_items_with_orderby_relevance_missing_search() {
 		$this->assertErrorResponse( 'rest_no_search_term_defined', $response, 400 );
 	}
 
-	public function test_get_items_ignore_sticky_posts_by_default() {
-		$this->markTestSkipped( 'Broken, see https://github.com/WP-API/WP-API/issues/2210' );
-		$post_id1 = $this->factory->post->create( array( 'post_status' => 'publish', 'post_date' => '2015-01-01 12:00:00', 'post_date_gmt' => '2015-01-01 12:00:00' ) );
-		$post_id2 = $this->factory->post->create( array( 'post_status' => 'publish', 'post_date' => '2015-01-02 12:00:00', 'post_date_gmt' => '2015-01-02 12:00:00' ) );
-		$post_id3 = $this->factory->post->create( array( 'post_status' => 'publish', 'post_date' => '2015-01-03 12:00:00', 'post_date_gmt' => '2015-01-03 12:00:00' ) );
-		stick_post( $post_id2 );
-
-		// No stickies by default
-		$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
-		$response = $this->server->dispatch( $request );
-		$data = $response->get_data();
-		$this->assertEquals( array( self::$post_id, $post_id3, $post_id2, $post_id1 ), wp_list_pluck( $data, 'id' ) );
-
-		// Permit stickies
-		$request = new WP_REST_Request( 'GET', '/wp/v2/posts' );
-		$request->set_param( 'ignore_sticky_posts', false );
-		$response = $this->server->dispatch( $request );
-		$data = $response->get_data();
-		$this->assertEquals( array( $post_id2, self::$post_id, $post_id3, $post_id1 ), wp_list_pluck( $data, 'id' ) );
-	}
-
 	public function test_get_items_offset_query() {
 		$id1 = self::$post_id;
 		$id2 = $this->factory->post->create( array( 'post_status' => 'publish' ) );
diff --git a/tests/phpunit/tests/rest-api/rest-users-controller.php b/tests/phpunit/tests/rest-api/rest-users-controller.php
index 1a81e42..44853aa 100644
--- a/tests/phpunit/tests/rest-api/rest-users-controller.php
+++ b/tests/phpunit/tests/rest-api/rest-users-controller.php
@@ -992,11 +992,10 @@ public function test_create_item_illegal_username() {
 		$this->assertEquals( 'Sorry, that username is not allowed.', $errors['username'] );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_create_new_network_user_on_site_does_not_add_user_to_sub_site() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test requires multisite.' );
-		}
-
 		$this->allow_user_to_manage_multisite();
 
 		$params = array(
@@ -1021,11 +1020,10 @@ public function test_create_new_network_user_on_site_does_not_add_user_to_sub_si
 		$this->assertFalse( $user_is_member );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_create_new_network_user_on_sub_site_adds_user_to_site() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test requires multisite.' );
-		}
-
 		$this->allow_user_to_manage_multisite();
 
 		$params = array(
@@ -1054,11 +1052,10 @@ public function test_create_new_network_user_on_sub_site_adds_user_to_site() {
 		$this->assertTrue( $user_is_member );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_create_existing_network_user_on_sub_site_has_error() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test requires multisite.' );
-		}
-
 		$this->allow_user_to_manage_multisite();
 
 		$params = array(
@@ -1441,11 +1438,10 @@ public function test_update_user_role_invalid_privilege_escalation() {
 		$this->assertArrayNotHasKey( 'administrator', $user->caps );
 	}
 
+	/**
+	 * @group ms-excluded
+	 */
 	public function test_update_user_role_invalid_privilege_deescalation() {
-		if ( is_multisite() ) {
-			return $this->markTestSkipped( 'Test only intended for single site.' );
-		}
-
 		$user_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
 
 		wp_set_current_user( $user_id );
@@ -1471,11 +1467,10 @@ public function test_update_user_role_invalid_privilege_deescalation() {
 		$this->assertArrayNotHasKey( 'editor', $user->caps );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_update_user_role_privilege_deescalation_multisite() {
-		if ( ! is_multisite() ) {
-			return $this->markTestSkipped( 'Test only intended for multisite.' );
-		}
-
 		$user_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
 
 		wp_set_current_user( $user_id );
@@ -2220,12 +2215,9 @@ public function test_additional_field_update_errors() {
 
 	/**
 	 * @ticket 39701
+	 * @group ms-required
 	 */
 	public function test_get_item_from_different_site_as_site_administrator() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		switch_to_blog( self::$site );
 		$user_id = $this->factory->user->create( array(
 			'role' => 'author',
@@ -2241,12 +2233,9 @@ public function test_get_item_from_different_site_as_site_administrator() {
 
 	/**
 	 * @ticket 39701
+	 * @group ms-required
 	 */
 	public function test_get_item_from_different_site_as_network_administrator() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		switch_to_blog( self::$site );
 		$user_id = $this->factory->user->create( array(
 			'role' => 'author',
@@ -2262,12 +2251,9 @@ public function test_get_item_from_different_site_as_network_administrator() {
 
 	/**
 	 * @ticket 39701
+	 * @group ms-required
 	 */
 	public function test_update_item_from_different_site_as_site_administrator() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		switch_to_blog( self::$site );
 		$user_id = $this->factory->user->create( array(
 			'role' => 'author',
@@ -2285,12 +2271,9 @@ public function test_update_item_from_different_site_as_site_administrator() {
 
 	/**
 	 * @ticket 39701
+	 * @group ms-required
 	 */
 	public function test_update_item_from_different_site_as_network_administrator() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		switch_to_blog( self::$site );
 		$user_id = $this->factory->user->create( array(
 			'role' => 'author',
@@ -2308,12 +2291,9 @@ public function test_update_item_from_different_site_as_network_administrator()
 
 	/**
 	 * @ticket 39701
+	 * @group ms-required
 	 */
 	public function test_delete_item_from_different_site_as_site_administrator() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		switch_to_blog( self::$site );
 		$user_id = $this->factory->user->create( array(
 			'role' => 'author',
@@ -2331,12 +2311,9 @@ public function test_delete_item_from_different_site_as_site_administrator() {
 
 	/**
 	 * @ticket 39701
+	 * @group ms-required
 	 */
 	public function test_delete_item_from_different_site_as_network_administrator() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-		}
-
 		switch_to_blog( self::$site );
 		$user_id = $this->factory->user->create( array(
 			'role' => 'author',
diff --git a/tests/phpunit/tests/rewrite.php b/tests/phpunit/tests/rewrite.php
index dfa9749..5f75149 100644
--- a/tests/phpunit/tests/rewrite.php
+++ b/tests/phpunit/tests/rewrite.php
@@ -119,12 +119,9 @@ function test_url_to_postid_set_url_scheme_http_to_https() {
 	/**
 	 * @ticket 35531
 	 * @group multisite
+	 * @group ms-required
 	 */
 	function test_url_to_postid_of_http_site_when_current_site_uses_https() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite' );
-		}
-
 		$_SERVER['HTTPS'] = 'on';
 
 		$network_home = home_url();
@@ -315,16 +312,9 @@ function test_url_to_postid_home_url_collision() {
 
 	/**
 	 * Reveals bug introduced in WP 3.0
-	 *
-	 * Run tests using multisite `phpunit -c multisite`
+	 * @group ms-required
 	 */
 	function test_url_to_postid_ms_home_url_collision() {
-
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'test_url_to_postid_ms_home_url_collision requires multisite' );
-			return false;
-		}
-
 		$blog_id = self::factory()->blog->create( array( 'path' => '/example' ) );
 		switch_to_blog( $blog_id );
 
diff --git a/tests/phpunit/tests/term/query.php b/tests/phpunit/tests/term/query.php
index 4a15f1e..b2e2a27 100644
--- a/tests/phpunit/tests/term/query.php
+++ b/tests/phpunit/tests/term/query.php
@@ -381,4 +381,50 @@ public function test_count_query_cache_should_be_invalidated_with_incrementor_bu
 		$count = $query->get_terms();
 		$this->assertEquals( 1, $count );
 	}
+
+	/**
+	 * @ticket 40496
+	 */
+	public function test_get_the_terms_should_respect_taxonomy_orderby() {
+		register_taxonomy( 'wptests_tax', 'post', array(
+			'sort' => true,
+			'args' => array(
+				'orderby' => 'term_order',
+			),
+		) );
+		$term_ids = self::factory()->term->create_many( 2, array(
+			'taxonomy' => 'wptests_tax',
+		) );
+		$post_id = self::factory()->post->create();
+		wp_set_object_terms( $post_id, array( $term_ids[0], $term_ids[1] ), 'wptests_tax' );
+		$terms = get_the_terms( $post_id, 'wptests_tax' );
+		$this->assertEquals( array( $term_ids[0], $term_ids[1] ), wp_list_pluck( $terms, 'term_id' ) );
+		// Flip the order
+		wp_set_object_terms( $post_id, array( $term_ids[1], $term_ids[0] ), 'wptests_tax' );
+		$terms = get_the_terms( $post_id, 'wptests_tax' );
+		$this->assertEquals( array( $term_ids[1], $term_ids[0] ), wp_list_pluck( $terms, 'term_id' ) );
+	}
+
+	/**
+	 * @ticket 40496
+	 */
+	public function test_wp_get_object_terms_should_respect_taxonomy_orderby() {
+		register_taxonomy( 'wptests_tax', 'post', array(
+			'sort' => true,
+			'args' => array(
+				'orderby' => 'term_order',
+			),
+		) );
+		$term_ids = self::factory()->term->create_many( 2, array(
+			'taxonomy' => 'wptests_tax',
+		) );
+		$post_id = self::factory()->post->create();
+		wp_set_object_terms( $post_id, array( $term_ids[0], $term_ids[1] ), 'wptests_tax' );
+		$terms = wp_get_object_terms( $post_id, array( 'category', 'wptests_tax' ) );
+		$this->assertEquals( array( $term_ids[0], $term_ids[1], 1 ), wp_list_pluck( $terms, 'term_id' ) );
+		// Flip the order
+		wp_set_object_terms( $post_id, array( $term_ids[1], $term_ids[0] ), 'wptests_tax' );
+		$terms = wp_get_object_terms( $post_id, array( 'category', 'wptests_tax' ) );
+		$this->assertEquals( array( $term_ids[1], $term_ids[0], 1 ), wp_list_pluck( $terms, 'term_id' ) );
+	}
 }
diff --git a/tests/phpunit/tests/theme.php b/tests/phpunit/tests/theme.php
index 2e76f7c..452a836 100644
--- a/tests/phpunit/tests/theme.php
+++ b/tests/phpunit/tests/theme.php
@@ -189,7 +189,7 @@ function test_wp_get_theme_contents() {
 	function test_default_theme_in_default_theme_list() {
 		$latest_default_theme = WP_Theme::get_core_default_theme();
 		if ( ! $latest_default_theme->exists() || 'twenty' !== substr( $latest_default_theme->get_stylesheet(), 0, 6 ) ) {
-			$this->markTestSkipped( 'No Twenty* series default themes are installed' ); 
+			$this->fail( 'No Twenty* series default themes are installed' ); 
 		}
 		$this->assertContains( $latest_default_theme->get_stylesheet(), $this->default_themes );
 	}
diff --git a/tests/phpunit/tests/theme/WPTheme.php b/tests/phpunit/tests/theme/WPTheme.php
index 5f01a25..da7374e 100644
--- a/tests/phpunit/tests/theme/WPTheme.php
+++ b/tests/phpunit/tests/theme/WPTheme.php
@@ -145,12 +145,9 @@ function test_display_method_on_get_method_failure() {
 	 * Enable a single theme on a network.
 	 *
 	 * @ticket 30594
+	 * @group ms-required
 	 */
 	function test_wp_theme_network_enable_single_theme() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite' );
-		}
-
 		$theme = 'testtheme-1';
 		$current_allowed_themes = get_site_option( 'allowedthemes' );
 		WP_Theme::network_enable_theme( $theme );
@@ -165,12 +162,9 @@ function test_wp_theme_network_enable_single_theme() {
 	 * Enable multiple themes on a network.
 	 *
 	 * @ticket 30594
+	 * @group ms-required
 	 */
 	function test_wp_theme_network_enable_multiple_themes() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite' );
-		}
-
 		$themes = array( 'testtheme-2', 'testtheme-3' );
 		$current_allowed_themes = get_site_option( 'allowedthemes' );
 		WP_Theme::network_enable_theme( $themes );
@@ -185,12 +179,9 @@ function test_wp_theme_network_enable_multiple_themes() {
 	 * Disable a single theme on a network.
 	 *
 	 * @ticket 30594
+	 * @group ms-required
 	 */
 	function test_network_disable_single_theme() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite' );
-		}
-
 		$current_allowed_themes = get_site_option( 'allowedthemes' );
 
 		$allowed_themes = array( 'existing-1' => true, 'existing-2' => true, 'existing-3' => true );
@@ -209,12 +200,9 @@ function test_network_disable_single_theme() {
 	 * Disable multiple themes on a network.
 	 *
 	 * @ticket 30594
+	 * @group ms-required
 	 */
 	function test_network_disable_multiple_themes() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test requires multisite' );
-		}
-
 		$current_allowed_themes = get_site_option( 'allowedthemes' );
 
 		$allowed_themes = array( 'existing-4' => true, 'existing-5' => true, 'existing-6' => true );
diff --git a/tests/phpunit/tests/upload.php b/tests/phpunit/tests/upload.php
index 361b2f0..3f2028d 100644
--- a/tests/phpunit/tests/upload.php
+++ b/tests/phpunit/tests/upload.php
@@ -8,10 +8,6 @@ class Tests_Upload extends WP_UnitTestCase {
 	var $siteurl;
 
 	function setUp() {
-		if ( is_multisite() ) {
-			$this->knownUTBug( 35 );
-		}
-
 		$this->_reset_options();
 		parent::setUp();
 	}
diff --git a/tests/phpunit/tests/user.php b/tests/phpunit/tests/user.php
index 3d38089..b2b87d1 100644
--- a/tests/phpunit/tests/user.php
+++ b/tests/phpunit/tests/user.php
@@ -649,12 +649,9 @@ function test_illegal_user_logins_single_wp_create_user( $user_login ) {
 
 	/**
 	 * @ticket 27317
+	 * @group ms-required
 	 */
 	function test_illegal_user_logins_multisite() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' requires multisite.' );
-		}
-
 		$user_data = array(
 			'user_login' => 'testuser',
 			'user_email' => 'testuser@example.com',
diff --git a/tests/phpunit/tests/user/capabilities.php b/tests/phpunit/tests/user/capabilities.php
index 88de406..2412704 100644
--- a/tests/phpunit/tests/user/capabilities.php
+++ b/tests/phpunit/tests/user/capabilities.php
@@ -226,6 +226,7 @@ function _meta_filter( $meta_value, $meta_key, $meta_type ) {
 			'manage_network_themes'  => array(),
 			'manage_network_options' => array(),
 			'delete_site'            => array(),
+			'upgrade_network'        => array(),
 
 			'setup_network'          => array( 'administrator' ),
 			'upload_plugins'         => array( 'administrator' ),
@@ -259,6 +260,7 @@ function _meta_filter( $meta_value, $meta_key, $meta_type ) {
 			'upload_plugins'         => array(),
 			'upload_themes'          => array(),
 			'edit_css'               => array(),
+			'upgrade_network'        => array(),
 
 			'customize'              => array( 'administrator' ),
 			'delete_site'            => array( 'administrator' ),
@@ -609,11 +611,10 @@ public function data_user_with_role_can_edit_others_posts() {
 		return $data;
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_super_admin_caps() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-			return;
-		}
 		$caps = $this->getAllCapsAndRoles();
 		$user = self::$super_admin;
 
@@ -1353,12 +1354,10 @@ function test_current_user_can_for_blog() {
 		wp_set_current_user( $old_uid );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_borked_current_user_can_for_blog() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-			return;
-		}
-
 		$orig_blog_id = get_current_blog_id();
 		$blog_id = self::factory()->blog->create();
 
@@ -1414,12 +1413,10 @@ function test_subscriber_cant_edit_posts() {
 		$this->assertFalse( current_user_can( 'edit_post', $post + 1 ) );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_multisite_administrator_can_not_edit_users() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-			return;
-		}
-
 		$user = self::$users['administrator'];
 		$other_user = self::$users['subscriber'];
 
@@ -1448,11 +1445,10 @@ public function test_only_admins_and_super_admins_can_remove_users() {
 		$this->assertFalse( user_can( self::$users['subscriber']->ID,   'remove_user', self::$users['subscriber']->ID ) );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_only_super_admins_can_delete_users_on_multisite() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs on multisite' );
-		}
-
 		$this->assertTrue( user_can( self::$super_admin->ID,             'delete_user', self::$users['subscriber']->ID ) );
 
 		$this->assertFalse( user_can( self::$users['administrator']->ID, 'delete_user', self::$users['subscriber']->ID ) );
@@ -1462,11 +1458,10 @@ public function test_only_super_admins_can_delete_users_on_multisite() {
 		$this->assertFalse( user_can( self::$users['subscriber']->ID,    'delete_user', self::$users['subscriber']->ID ) );
 	}
 
+	/**
+	 * @group ms-excluded
+	 */
 	public function test_only_admins_can_delete_users_on_single_site() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run on multisite' );
-		}
-
 		$this->assertTrue( user_can( self::$users['administrator']->ID, 'delete_user', self::$users['subscriber']->ID ) );
 
 		$this->assertFalse( user_can( self::$users['editor']->ID,       'delete_user', self::$users['subscriber']->ID ) );
@@ -1520,12 +1515,10 @@ function test_contributor_cannot_edit_scheduled_post() {
 
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_multisite_administrator_with_manage_network_users_can_edit_users() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-			return;
-		}
-
 		$user = self::$users['administrator'];
 		$user->add_cap( 'manage_network_users' );
 		$other_user = self::$users['subscriber'];
@@ -1539,12 +1532,10 @@ function test_multisite_administrator_with_manage_network_users_can_edit_users()
 		$this->assertTrue( $can_edit_user );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_multisite_administrator_with_manage_network_users_can_not_edit_super_admin() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite' );
-			return;
-		}
-
 		$user = self::$users['administrator'];
 		$user->add_cap( 'manage_network_users' );
 
@@ -1559,6 +1550,7 @@ function test_multisite_administrator_with_manage_network_users_can_not_edit_sup
 
 	/**
 	 * @ticket 16956
+	 * @expectedIncorrectUsage map_meta_cap
 	 */
 	function test_require_edit_others_posts_if_post_type_doesnt_exist() {
 		register_post_type( 'existed' );
@@ -1568,7 +1560,6 @@ function test_require_edit_others_posts_if_post_type_doesnt_exist() {
 		$subscriber_id = self::$users['subscriber']->ID;
 		$editor_id = self::$users['editor']->ID;
 
-		$this->setExpectedIncorrectUsage( 'map_meta_cap' );
 		foreach ( array( 'delete_post', 'edit_post', 'read_post', 'publish_post' ) as $cap ) {
 			wp_set_current_user( $subscriber_id );
 			$this->assertSame( array( 'edit_others_posts' ), map_meta_cap( $cap, $subscriber_id, $post_id ) );
@@ -1636,6 +1627,10 @@ public function testNonLoggedInUsersHaveNoCapabilities() {
 			$this->assertFalse( current_user_can( $cap ), "Non-logged-in user should not have the {$cap} capability" );
 		}
 
+		// Special cases for link manager and unfiltered uploads:
+		$this->assertFalse( current_user_can( 'manage_links' ), "Non-logged-in user should not have the manage_links capability" );
+		$this->assertFalse( current_user_can( 'unfiltered_upload' ), "Non-logged-in user should not have the unfiltered_upload capability" );
+
 		$this->assertFalse( current_user_can( 'start_a_fire' ), "Non-logged-in user should not have a custom capability" );
 		$this->assertFalse( current_user_can( 'do_not_allow' ), "Non-logged-in user should not have the do_not_allow capability" );
 	}
@@ -1762,12 +1757,9 @@ public function test_user_cannot_add_user_meta() {
 
 	/**
 	 * @ticket 39063
+	 * @group ms-required
 	 */
 	public function test_only_super_admins_can_remove_themselves_on_multisite() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test only runs in multisite.' );
-		}
-
 		$this->assertTrue( user_can( self::$super_admin->ID, 'remove_user', self::$super_admin->ID ) );
 
 		$this->assertFalse( user_can( self::$users['administrator']->ID, 'remove_user', self::$users['administrator']->ID ) );
diff --git a/tests/phpunit/tests/user/countUsers.php b/tests/phpunit/tests/user/countUsers.php
index d3192bb..f5fa6b9 100644
--- a/tests/phpunit/tests/user/countUsers.php
+++ b/tests/phpunit/tests/user/countUsers.php
@@ -9,13 +9,9 @@ class Tests_User_CountUsers extends WP_UnitTestCase {
 	 * @ticket 22993
 	 *
 	 * @dataProvider data_count_users_strategies
+	 * @group ms-excluded
 	 */
 	public function test_count_users_is_accurate( $strategy ) {
-
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run on multisite' );
-		}
-
 		// Setup users
 		$admin = self::factory()->user->create( array(
 			'role' => 'administrator',
@@ -57,15 +53,11 @@ public function test_count_users_is_accurate( $strategy ) {
 	/**
 	 * @ticket 22993
 	 * @group multisite
+	 * @group ms-required
 	 *
 	 * @dataProvider data_count_users_strategies
 	 */
 	public function test_count_users_multisite_is_accurate( $strategy ) {
-
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test requires multisite' );
-		}
-
 		// Setup users
 		$admin = self::factory()->user->create( array(
 			'role' => 'administrator',
@@ -175,6 +167,31 @@ public function test_count_users_is_accurate_with_multiple_roles( $strategy ) {
 
 	}
 
+	/**
+	 * @ticket 29785
+	 *
+	 * @dataProvider data_count_users_strategies
+	 */
+	public function test_count_users_should_not_count_users_who_are_not_in_posts_table( $strategy ) {
+		global $wpdb;
+
+		// Get a 'before' count for comparison.
+		$count = count_users( $strategy );
+
+		$u = self::factory()->user->create( array(
+			'role' => 'editor',
+		) );
+
+		// Manually delete the user, but leave the capabilities usermeta.
+		$wpdb->delete( $wpdb->users, array(
+			'ID' => $u,
+		) );
+
+		$count2 = count_users( $strategy );
+
+		$this->assertEqualSets( $count, $count2 );
+	}
+
 	function data_count_users_strategies() {
 		return array(
 			array(
diff --git a/tests/phpunit/tests/user/mapMetaCap.php b/tests/phpunit/tests/user/mapMetaCap.php
index c017293..f98c87b 100644
--- a/tests/phpunit/tests/user/mapMetaCap.php
+++ b/tests/phpunit/tests/user/mapMetaCap.php
@@ -213,8 +213,10 @@ function test_capability_type_post_with_all_meta_caps_renamed_mapped() {
 	}
 
 	function test_unfiltered_html_cap() {
-		if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML )
-			$this->markTestSkipped( 'DISALLOW_UNFILTERED_HTML is defined.' );
+		if ( defined( 'DISALLOW_UNFILTERED_HTML' ) ) {
+			$this->assertFalse( DISALLOW_UNFILTERED_HTML );
+		}
+
 		if ( is_multisite() ) {
 			$this->assertEquals( array( 'do_not_allow' ), map_meta_cap( 'unfiltered_html', 0 ) );
 			$this->assertEquals( array( 'unfiltered_html' ), map_meta_cap( 'unfiltered_html', self::$user_id ) );
@@ -227,16 +229,14 @@ function test_unfiltered_html_cap() {
 	 * @ticket 20488
 	 */
 	function test_file_edit_caps_not_reliant_on_unfiltered_html_constant() {
-		if ( defined( 'DISALLOW_FILE_MODS' ) || defined( 'DISALLOW_FILE_EDIT' ) )
-			$this->markTestSkipped('DISALLOW_FILE_MODS or DISALLOW_FILE_EDIT is defined.');
+		$this->assertFalse( defined( 'DISALLOW_FILE_MODS' ) );
+		$this->assertFalse( defined( 'DISALLOW_FILE_EDIT' ) );
 
-		if ( defined( 'DISALLOW_UNFILTERED_HTML' ) ) {
-			if ( ! DISALLOW_UNFILTERED_HTML )
-				$this->markTestSkipped( 'DISALLOW_UNFILTERED_HTML is defined.' );
-		} else {
+		if ( ! defined( 'DISALLOW_UNFILTERED_HTML' ) ) {
 			define( 'DISALLOW_UNFILTERED_HTML', true );
 		}
 
+		$this->assertTrue( DISALLOW_UNFILTERED_HTML );
 		$this->assertEquals( array( 'update_core' ), map_meta_cap( 'update_core', self::$user_id ) );
 		$this->assertEquals( array( 'edit_plugins' ), map_meta_cap( 'edit_plugins', self::$user_id ) );
 	}
diff --git a/tests/phpunit/tests/user/query.php b/tests/phpunit/tests/user/query.php
index 2cadc91..ee2f3d8 100644
--- a/tests/phpunit/tests/user/query.php
+++ b/tests/phpunit/tests/user/query.php
@@ -551,11 +551,10 @@ public function test_roles_and_caps_should_be_populated_for_default_value_of_blo
 		$this->assertSame( array( 'author' => true ), $user->caps );
 	}
 
+	/**
+	 * @group ms-excluded
+	 */
 	public function test_roles_and_caps_should_be_populated_for_explicit_value_of_blog_id_on_nonms() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' is a non-multisite-only test.' );
-		}
-
 		$query = new WP_User_Query( array(
 			'include' => self::$author_ids[0],
 			'blog_id' => get_current_blog_id(),
@@ -569,11 +568,10 @@ public function test_roles_and_caps_should_be_populated_for_explicit_value_of_bl
 		$this->assertSame( array( 'author' => true ), $user->caps );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_roles_and_caps_should_be_populated_for_explicit_value_of_current_blog_id_on_ms() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' is a multisite-only test.' );
-		}
-
 		$query = new WP_User_Query( array(
 			'include' => self::$author_ids[0],
 			'blog_id' => get_current_blog_id(),
@@ -587,11 +585,10 @@ public function test_roles_and_caps_should_be_populated_for_explicit_value_of_cu
 		$this->assertSame( array( 'author' => true ), $user->caps );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	public function test_roles_and_caps_should_be_populated_for_explicit_value_of_different_blog_id_on_ms_when_fields_all_with_meta() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' is a multisite-only test.' );
-		}
-
 		$b = self::factory()->blog->create();
 
 		add_user_to_blog( $b, self::$author_ids[0], 'author' );
@@ -612,12 +609,9 @@ public function test_roles_and_caps_should_be_populated_for_explicit_value_of_di
 
 	/**
 	 * @ticket 31878
+	 * @group ms-required
 	 */
 	public function test_roles_and_caps_should_be_populated_for_explicit_value_of_different_blog_id_on_ms_when_fields_all() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' is a multisite-only test.' );
-		}
-
 		$b = self::factory()->blog->create();
 		add_user_to_blog( $b, self::$author_ids[0], 'author' );
 
@@ -637,12 +631,9 @@ public function test_roles_and_caps_should_be_populated_for_explicit_value_of_di
 
 	/**
 	 * @ticket 32019
+	 * @group ms-required
 	 */
 	public function test_who_authors() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' requires multisite.' );
-		}
-
 		$b = self::factory()->blog->create();
 
 		add_user_to_blog( $b, self::$author_ids[0], 'subscriber' );
@@ -663,12 +654,9 @@ public function test_who_authors() {
 
 	/**
 	 * @ticket 32019
+	 * @group ms-required
 	 */
 	public function test_who_authors_should_work_alongside_meta_query() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' requires multisite.' );
-		}
-
 		$b = self::factory()->blog->create();
 
 		add_user_to_blog( $b, self::$author_ids[0], 'subscriber' );
@@ -698,12 +686,9 @@ public function test_who_authors_should_work_alongside_meta_query() {
 
 	/**
 	 * @ticket 36724
+	 * @group ms-required
 	 */
 	public function test_who_authors_should_work_alongside_meta_params() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' requires multisite.' );
-		}
-
 		$b = self::factory()->blog->create();
 
 		add_user_to_blog( $b, self::$author_ids[0], 'subscriber' );
@@ -791,12 +776,9 @@ public function test_has_published_posts_should_ignore_non_published_posts() {
 
 	/**
 	 * @ticket 32250
+	 * @group ms-required
 	 */
 	public function test_has_published_posts_should_respect_blog_id() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' requires multisite.' );
-		}
-
 		$blogs = self::factory()->blog->create_many( 2 );
 
 		add_user_to_blog( $blogs[0], self::$author_ids[0], 'author' );
@@ -1358,12 +1340,9 @@ public function test_role__not_in_user_without_role() {
 
 	/**
 	 * @ticket 22212
+	 * @group ms-required
 	 */
 	public function test_blog_id_should_restrict_by_blog_without_requiring_a_named_role() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' requires multisite.' );
-		}
-
 		$sites = self::factory()->blog->create_many( 2 );
 
 		add_user_to_blog( $sites[0], self::$author_ids[0], 'author' );
@@ -1380,12 +1359,9 @@ public function test_blog_id_should_restrict_by_blog_without_requiring_a_named_r
 	/**
 	 * @ticket 22212
 	 * @ticket 21119
+	 * @group ms-required
 	 */
 	public function test_calling_prepare_query_a_second_time_should_not_add_another_cap_query_on_multisite() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( __METHOD__ . ' requires Multisite.' );
-		}
-
 		$site_id = get_current_blog_id();
 		add_user_to_blog( $site_id, self::$author_ids[0], 'author' );
 
diff --git a/tests/phpunit/tests/user/wpAuthenticateSpamCheck.php b/tests/phpunit/tests/user/wpAuthenticateSpamCheck.php
index 7f3b164..78cbdcd 100644
--- a/tests/phpunit/tests/user/wpAuthenticateSpamCheck.php
+++ b/tests/phpunit/tests/user/wpAuthenticateSpamCheck.php
@@ -4,11 +4,11 @@
  * @group user
  */
 class Tests_User_WpAuthenticateSpamCheck extends WP_UnitTestCase {
-	function test_wp_authenticate_spam_check_returns_user_when_single_site() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'This test applies to single site only.' );
-		}
 
+	/**
+	 * @group ms-excluded
+	 */
+	function test_wp_authenticate_spam_check_returns_user_when_single_site() {
 		$user_id = self::factory()->user->create( array( 'role' => 'contributor' ) );
 		$user = new WP_User( $user_id );
 		$actual_user = wp_authenticate_spam_check( $user );
@@ -17,11 +17,10 @@ function test_wp_authenticate_spam_check_returns_user_when_single_site() {
 		$this->assertEquals( $user->user_login, $actual_user->user_login );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_wp_authenticate_spam_check_returns_user_when_not_flagged() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test applies to multisite only.' );
-		}
-
 		$user_id = self::factory()->user->create( array( 'role' => 'contributor' ) );
 		$user = new WP_User( $user_id );
 		$actual_user = wp_authenticate_spam_check( $user );
@@ -30,11 +29,10 @@ function test_wp_authenticate_spam_check_returns_user_when_not_flagged() {
 		$this->assertEquals( $user->user_login, $actual_user->user_login );
 	}
 
+	/**
+	 * @group ms-required
+	 */
 	function test_wp_authenticate_spam_check_returns_wp_error_when_flagged() {
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'This test applies to multisite only.' );
-		}
-
 		$user_id = self::factory()->user->create( array( 'role' => 'contributor' ) );
 		update_user_status( $user_id, 'spam', 1 );
 		$user = new WP_User( $user_id );
diff --git a/tests/phpunit/tests/user/wpDeleteUser.php b/tests/phpunit/tests/user/wpDeleteUser.php
index 60e7caf..7f7e902 100644
--- a/tests/phpunit/tests/user/wpDeleteUser.php
+++ b/tests/phpunit/tests/user/wpDeleteUser.php
@@ -123,11 +123,10 @@ function test_wp_delete_user_reassignment_clears_post_caches() {
 		$this->assertEquals( $reassign, $post->post_author );
 	}
 
+	/**
+	 * @group ms-excluded
+	 */
 	public function test_numeric_string_user_id() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'wp_delete_user() does not delete user records in Multisite.' );
-		}
-
 		$u = self::factory()->user->create();
 
 		$u_string = (string) $u;
@@ -144,12 +143,9 @@ public function test_should_return_false_for_non_numeric_string_user_id() {
 
 	/**
 	 * @ticket 33800
+	 * @group ms-excluded
 	 */
 	public function test_should_return_false_for_object_user_id() {
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'wp_delete_user() does not delete user records in Multisite.' );
-		}
-
 		$u_obj = self::factory()->user->create_and_get();
 		$this->assertFalse( wp_delete_user( $u_obj ) );
 		$this->assertEquals( $u_obj->ID, username_exists( $u_obj->user_login ) );
diff --git a/tests/phpunit/tests/user/wpGetUsersWithNoRole.php b/tests/phpunit/tests/user/wpGetUsersWithNoRole.php
index cd7b118..82c0da7 100644
--- a/tests/phpunit/tests/user/wpGetUsersWithNoRole.php
+++ b/tests/phpunit/tests/user/wpGetUsersWithNoRole.php
@@ -7,13 +7,9 @@ class Tests_User_GetUsersWithNoRole extends WP_UnitTestCase {
 
 	/**
 	 * @ticket 22993
+	 * @group ms-excluded
 	 */
 	public function test_get_users_with_no_role_is_accurate() {
-
-		if ( is_multisite() ) {
-			$this->markTestSkipped( 'Test does not run on multisite' );
-		}
-
 		// Setup users
 		$admin = self::factory()->user->create( array(
 			'role' => 'administrator',
@@ -41,13 +37,9 @@ public function test_get_users_with_no_role_is_accurate() {
 	/**
 	 * @ticket 22993
 	 * @group multisite
+	 * @group ms-required
 	 */
 	public function test_get_users_with_no_role_multisite_is_accurate() {
-
-		if ( ! is_multisite() ) {
-			$this->markTestSkipped( 'Test requires multisite' );
-		}
-
 		// Setup users
 		$admin = self::factory()->user->create( array(
 			'role' => 'administrator',
diff --git a/tests/phpunit/tests/xmlrpc/basic.php b/tests/phpunit/tests/xmlrpc/basic.php
index bbbcbd7..7818fdb 100644
--- a/tests/phpunit/tests/xmlrpc/basic.php
+++ b/tests/phpunit/tests/xmlrpc/basic.php
@@ -11,7 +11,7 @@ class Tests_XMLRPC_Basic extends WP_XMLRPC_UnitTestCase {
 	function test_enabled() {
 		$result = $this->myxmlrpcserver->wp_getOptions( array( 1, 'username', 'password' ) );
 
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		// If disabled, 405 would result.
 		$this->assertEquals( 403, $result->code );
 	}
diff --git a/tests/phpunit/tests/xmlrpc/mt/getRecentPostTitles.php b/tests/phpunit/tests/xmlrpc/mt/getRecentPostTitles.php
index 8d9c027..7c36f97 100644
--- a/tests/phpunit/tests/xmlrpc/mt/getRecentPostTitles.php
+++ b/tests/phpunit/tests/xmlrpc/mt/getRecentPostTitles.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_mt_getRecentPostTitles extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->mt_getRecentPostTitles( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -15,7 +15,7 @@ function test_no_posts() {
 		$this->make_user_by_role( 'author' );
 
 		$result = $this->myxmlrpcserver->mt_getRecentPostTitles( array( 1, 'author', 'author' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 	}
 
@@ -25,7 +25,7 @@ function test_no_editable_posts() {
 		self::factory()->post->create( array( 'post_author' => $editor ) );
 
 		$result = $this->myxmlrpcserver->mt_getRecentPostTitles( array( 1, 'author', 'author' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( 0, count( $result ) );
 	}
 
@@ -35,7 +35,7 @@ function test_date() {
 		self::factory()->post->create();
 
 		$results = $this->myxmlrpcserver->mt_getRecentPostTitles( array( 1, 'author', 'author' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $result ) {
 			$post = get_post( $result['postid'] );
diff --git a/tests/phpunit/tests/xmlrpc/mw/editPost.php b/tests/phpunit/tests/xmlrpc/mw/editPost.php
index e265b88..39a937d 100644
--- a/tests/phpunit/tests/xmlrpc/mw/editPost.php
+++ b/tests/phpunit/tests/xmlrpc/mw/editPost.php
@@ -8,7 +8,7 @@ class Tests_XMLRPC_mw_editPost extends WP_XMLRPC_UnitTestCase {
 	function test_invalid_username_password() {
 		$post = array();
 		$result = $this->myxmlrpcserver->mw_editPost( array( 1, 'username', 'password', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -20,7 +20,7 @@ function test_edit_own_post() {
 		$new_title = 'Post test (updated)';
 		$post2 = array( 'title' => $new_title );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'contributor', 'contributor', $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -37,7 +37,7 @@ function test_capable_edit_others_post() {
 		$new_title = 'Post test (updated)';
 		$post2 = array( 'title' => $new_title );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'editor', 'editor', $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -55,7 +55,7 @@ function test_incapable_edit_others_post() {
 		$new_title = 'Post test (updated)';
 		$post2 = array( 'title' => $new_title );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'contributor', 'contributor', $post2 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 
 		$out = get_post( $post_id );
@@ -72,7 +72,7 @@ function test_capable_reassign_author() {
 
 		$post2 = array( 'wp_author_id' => $author_id );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'editor', 'editor', $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -88,7 +88,7 @@ function test_incapable_reassign_author() {
 
 		$post2 = array( 'wp_author_id' => $author_id );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'contributor', 'contributor', $post2 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 
 		$out = get_post( $post_id );
@@ -107,7 +107,7 @@ function test_capable_reassign_author_to_self() {
 
 		$post2 = array( 'wp_author_id' => $editor_id );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'editor', 'editor', $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -131,13 +131,13 @@ function test_post_thumbnail() {
 		// add post thumbnail to post that does not have one
 		$post2 = array( 'wp_post_thumbnail' => $attachment_id );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'author', 'author', $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $attachment_id, get_post_meta( $post_id, '_thumbnail_id', true ) );
 
 		// edit the post without supplying a post_thumbnail and check that it didn't change
 		$post3 = array( 'post_content' => 'Updated post' );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'author', 'author', $post3 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $attachment_id, get_post_meta( $post_id, '_thumbnail_id', true ) );
 
 		// create another attachment
@@ -146,13 +146,13 @@ function test_post_thumbnail() {
 		// change the post's post_thumbnail
 		$post4 = array( 'wp_post_thumbnail' => $attachment2_id );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'author', 'author', $post4 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $attachment2_id, get_post_meta( $post_id, '_thumbnail_id', true ) );
 
 		// unset the post's post_thumbnail
 		$post5 = array( 'wp_post_thumbnail' => '' );
 		$result = $this->myxmlrpcserver->mw_editPost( array($post_id, 'author', 'author', $post5 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( '', get_post_meta( $post_id, '_thumbnail_id', true ) );
 
 		remove_theme_support( 'post-thumbnails' );
@@ -166,7 +166,7 @@ function test_edit_basic_post_info() {
 
 		$post2 = array( 'title' => 'New Title', 'post_author' => $contributor_id );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'contributor', 'contributor', $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -174,7 +174,7 @@ function test_edit_basic_post_info() {
 
 		$post3 = array( 'description' => 'New Content', 'post_author' => $contributor_id );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'contributor', 'contributor', $post3 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -183,7 +183,7 @@ function test_edit_basic_post_info() {
 
 		$post4 = array( 'mt_excerpt' => 'New Excerpt', 'post_author' => $contributor_id );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'contributor', 'contributor', $post4 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -202,7 +202,7 @@ function test_make_post_sticky() {
 		$post_id = wp_insert_post( $post );
 
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'editor', 'editor', array( 'sticky' => '1' ) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue( $result );
 	}
 
@@ -215,7 +215,7 @@ function test_change_post_type() {
 
 		$post2 = array( 'post_type' => 'page' );
 		$result = $this->myxmlrpcserver->mw_editPost( array( $post_id, 'contributor', 'contributor', $post2 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( $result->code, 401 );
 	}
 
diff --git a/tests/phpunit/tests/xmlrpc/mw/getPost.php b/tests/phpunit/tests/xmlrpc/mw/getPost.php
index 0d433d0..924d6ff 100644
--- a/tests/phpunit/tests/xmlrpc/mw/getPost.php
+++ b/tests/phpunit/tests/xmlrpc/mw/getPost.php
@@ -19,7 +19,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->mw_getPost( array( self::$post_id, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -27,7 +27,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->mw_getPost( array( self::$post_id, 'subscriber', 'subscriber' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -36,7 +36,7 @@ function test_incapable_user() {
 	 */
 	function test_invalid_postid() {
 		$result = $this->myxmlrpcserver->mw_getPost( array( 9999, 'author', 'author' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 	}
 
@@ -45,7 +45,7 @@ function test_valid_post() {
 
 		$fields = array( 'post' );
 		$result = $this->myxmlrpcserver->mw_getPost( array( self::$post_id, 'author', 'author' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Check data types
 		$this->assertInternalType( 'string', $result['userid'] );
@@ -96,7 +96,7 @@ function test_post_thumbnail() {
 
 		$fields = array( 'post' );
 		$result = $this->myxmlrpcserver->mw_getPost( array( self::$post_id, 'author', 'author' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		$this->assertInternalType( 'string', $result['wp_post_thumbnail'] );
 		$this->assertStringMatchesFormat( '%d', $result['wp_post_thumbnail'] );
@@ -108,7 +108,7 @@ function test_post_thumbnail() {
 	function test_date() {
 		$fields = array( 'post' );
 		$result = $this->myxmlrpcserver->mw_getPost( array( self::$post_id, 'author', 'author' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		$this->assertInstanceOf( 'IXR_Date', $result['dateCreated'] );
 		$this->assertInstanceOf( 'IXR_Date', $result['date_created_gmt'] );
diff --git a/tests/phpunit/tests/xmlrpc/mw/getRecentPosts.php b/tests/phpunit/tests/xmlrpc/mw/getRecentPosts.php
index a3b7cd4..8ab7627 100644
--- a/tests/phpunit/tests/xmlrpc/mw/getRecentPosts.php
+++ b/tests/phpunit/tests/xmlrpc/mw/getRecentPosts.php
@@ -20,7 +20,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->mw_getRecentPosts( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -31,7 +31,7 @@ function test_no_editing_privileges() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->mw_getRecentPosts( array( 1, 'subscriber', 'subscriber' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -39,7 +39,7 @@ function test_no_editable_posts() {
 		wp_delete_post( self::$post_id, true );
 
 		$result = $this->myxmlrpcserver->mw_getRecentPosts( array( 1, 'author', 'author' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( 0, count( $result ) );
 	}
 
@@ -48,7 +48,7 @@ function test_valid_post() {
 
 		$fields = array( 'post' );
 		$results = $this->myxmlrpcserver->mw_getRecentPosts( array( 1, 'author', 'author' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $result ) {
 			$post = get_post( $result['postid'] );
@@ -99,7 +99,7 @@ function test_post_thumbnail() {
 		set_post_thumbnail( self::$post_id, $attachment_id );
 
 		$results = $this->myxmlrpcserver->mw_getRecentPosts( array( self::$post_id, 'author', 'author' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $result ) {
 			$this->assertInternalType( 'string', $result['wp_post_thumbnail'] );
@@ -119,7 +119,7 @@ function test_date() {
 		$this->make_user_by_role( 'editor' );
 
 		$results = $this->myxmlrpcserver->mw_getRecentPosts( array( 1, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $result ) {
 			$post = get_post( $result['postid'] );
diff --git a/tests/phpunit/tests/xmlrpc/mw/newPost.php b/tests/phpunit/tests/xmlrpc/mw/newPost.php
index 01316e6..06aaafc 100644
--- a/tests/phpunit/tests/xmlrpc/mw/newPost.php
+++ b/tests/phpunit/tests/xmlrpc/mw/newPost.php
@@ -8,7 +8,7 @@ class Tests_XMLRPC_mw_newPost extends WP_XMLRPC_UnitTestCase {
 	function test_invalid_username_password() {
 		$post = array();
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'username', 'password', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -17,7 +17,7 @@ function test_incapable_user() {
 
 		$post = array();
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'subscriber', 'subscriber', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -26,7 +26,7 @@ function test_no_content() {
 
 		$post = array();
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 		$this->assertEquals( 'Content, title, and excerpt are empty.', $result->message );
 	}
@@ -36,7 +36,7 @@ function test_basic_content() {
 
 		$post = array( 'title' => 'Test' );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 	}
 
@@ -45,7 +45,7 @@ function test_ignore_id() {
 
 		$post = array( 'title' => 'Test', 'ID' => 103948 );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertNotEquals( '103948', $result );
 	}
 
@@ -54,7 +54,7 @@ function test_capable_publish() {
 
 		$post = array( 'title' => 'Test', 'post_status' => 'publish' );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_incapable_publish() {
@@ -62,7 +62,7 @@ function test_incapable_publish() {
 
 		$post = array( 'title' => 'Test', 'post_status' => 'publish' );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'contributor', 'contributor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -72,7 +72,7 @@ function test_capable_other_author() {
 
 		$post = array( 'title' => 'Test', 'wp_author_id' => $other_author_id );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_incapable_other_author() {
@@ -81,7 +81,7 @@ function test_incapable_other_author() {
 
 		$post = array( 'title' => 'Test', 'wp_author_id' => $other_author_id );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'contributor', 'contributor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -93,7 +93,7 @@ function test_invalid_author() {
 
 		$post = array( 'title' => 'Test', 'wp_author_id' => 99999999 );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 	}
 
@@ -102,7 +102,7 @@ function test_empty_author() {
 
 		$post = array( 'title' => 'Test' );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 
 		$out = get_post( $result );
@@ -121,7 +121,7 @@ function test_post_thumbnail() {
 
 		$post = array( 'title' => 'Post Thumbnail Test', 'wp_post_thumbnail' => $attachment_id );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $attachment_id, get_post_meta( $result, '_thumbnail_id', true ) );
 
 		remove_theme_support( 'post-thumbnails' );
@@ -132,7 +132,7 @@ function test_incapable_set_post_type_as_page() {
 
 		$post = array( 'title' => 'Test', 'post_type' => 'page' );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -141,7 +141,7 @@ function test_capable_set_post_type_as_page() {
 
 		$post = array( 'title' => 'Test', 'post_type' => 'page' );
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 
 		$out = get_post( $result );
@@ -162,7 +162,7 @@ function test_draft_post_date() {
 			'post_status' => 'draft'
 		);
 		$result = $this->myxmlrpcserver->mw_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 
 		$out = get_post( $result );
diff --git a/tests/phpunit/tests/xmlrpc/wp/deletePost.php b/tests/phpunit/tests/xmlrpc/wp/deletePost.php
index e7284a7..d2a5bc6 100644
--- a/tests/phpunit/tests/xmlrpc/wp/deletePost.php
+++ b/tests/phpunit/tests/xmlrpc/wp/deletePost.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_deletePost extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_deletePost( array( 1, 'username', 'password', 0 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -15,7 +15,7 @@ function test_invalid_post() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_deletePost( array( 1, 'editor', 'editor', 340982340 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 	}
 
@@ -24,7 +24,7 @@ function test_incapable_user() {
 		$post_id = self::factory()->post->create();
 
 		$result = $this->myxmlrpcserver->wp_deletePost( array( 1, 'subscriber', 'subscriber', $post_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -33,7 +33,7 @@ function test_post_deleted() {
 		$post_id = self::factory()->post->create();
 
 		$result = $this->myxmlrpcserver->wp_deletePost( array( 1, 'editor', 'editor', $post_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue( $result );
 
 		$post = get_post( $post_id );
diff --git a/tests/phpunit/tests/xmlrpc/wp/deleteTerm.php b/tests/phpunit/tests/xmlrpc/wp/deleteTerm.php
index ee95c6a..94e25c9 100644
--- a/tests/phpunit/tests/xmlrpc/wp/deleteTerm.php
+++ b/tests/phpunit/tests/xmlrpc/wp/deleteTerm.php
@@ -14,7 +14,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_deleteTerm( array( 1, 'username', 'password', 'category', 0 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -22,7 +22,7 @@ function test_empty_taxonomy() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_deleteTerm( array( 1, 'subscriber', 'subscriber', '', 0 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -31,7 +31,7 @@ function test_invalid_taxonomy() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_deleteTerm( array( 1, 'subscriber', 'subscriber', 'not_existing', 0 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -40,7 +40,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_deleteTerm( array( 1, 'subscriber', 'subscriber', 'category', self::$term_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 		$this->assertEquals( __( 'Sorry, you are not allowed to delete this term.' ), $result->message );
 	}
@@ -49,7 +49,7 @@ function test_empty_term() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_deleteTerm( array( 1, 'editor', 'editor', 'category', '' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 		$this->assertEquals( __('Empty Term'), $result->message );
 	}
@@ -58,7 +58,7 @@ function test_invalid_term() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_deleteTerm( array( 1, 'editor', 'editor', 'category', 9999 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 		$this->assertEquals( __( 'Invalid term ID.' ), $result->message );
 	}
@@ -67,7 +67,7 @@ function test_term_deleted() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_deleteTerm( array( 1, 'editor', 'editor', 'category', self::$term_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertInternalType( 'boolean', $result );
 	}
 }
diff --git a/tests/phpunit/tests/xmlrpc/wp/editComment.php b/tests/phpunit/tests/xmlrpc/wp/editComment.php
index b43beb6..fcb3bb5 100644
--- a/tests/phpunit/tests/xmlrpc/wp/editComment.php
+++ b/tests/phpunit/tests/xmlrpc/wp/editComment.php
@@ -22,7 +22,7 @@ function test_author_can_edit_own_comment() {
 		$result = $this->myxmlrpcserver->wp_editComment( array( 1, 'author', 'author', $comment_id, array(
 			'status' => 'hold'
 		) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue( $result );
 	}
 
@@ -42,7 +42,7 @@ function test_author_cannot_edit_others_comment() {
 		) );
 
 		$result = $this->myxmlrpcserver->wp_editComment( array( 1, 'author', 'author', $comment_id, array( 'status' => 'hold' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Sorry, you are not allowed to moderate or edit this comment.' ), $result->message );
 	}
diff --git a/tests/phpunit/tests/xmlrpc/wp/editPost.php b/tests/phpunit/tests/xmlrpc/wp/editPost.php
index 373d458..55f5be9 100644
--- a/tests/phpunit/tests/xmlrpc/wp/editPost.php
+++ b/tests/phpunit/tests/xmlrpc/wp/editPost.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_editPost extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'username', 'password', 0, array() ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -20,7 +20,7 @@ function test_edit_own_post() {
 		$new_title = 'Post test (updated)';
 		$post2 = array( 'post_title' => $new_title );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'contributor', 'contributor', $post_id, $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -37,7 +37,7 @@ function test_capable_edit_others_post() {
 		$new_title = 'Post test (updated)';
 		$post2 = array( 'post_title' => $new_title );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -55,7 +55,7 @@ function test_incapable_edit_others_post() {
 		$new_title = 'Post test (updated)';
 		$post2 = array( 'post_title' => $new_title );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'contributor', 'contributor', $post_id, $post2 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 
 		$out = get_post( $post_id );
@@ -72,7 +72,7 @@ function test_capable_reassign_author() {
 
 		$post2 = array( 'post_author' => $author_id );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -88,7 +88,7 @@ function test_incapable_reassign_author() {
 
 		$post2 = array( 'post_author' => $author_id );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'contributor', 'contributor', $post_id, $post2 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 
 		$out = get_post( $post_id );
@@ -107,7 +107,7 @@ function test_capable_reassign_author_to_self() {
 
 		$post2 = array( 'post_author' => $editor_id );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -131,12 +131,12 @@ function test_post_thumbnail() {
 		// add post thumbnail to post that does not have one
 		$post2 = array( 'post_thumbnail' => $attachment_id );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'author', 'author', $post_id, $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $attachment_id, get_post_meta( $post_id, '_thumbnail_id', true ) );
 
 		// fetch the post to verify that it appears
 		$result = $this->myxmlrpcserver->wp_getPost( array( 1, 'author', 'author', $post_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertArrayHasKey( 'post_thumbnail', $result );
 		$this->assertInternalType( 'array', $result['post_thumbnail'] );
 		$this->assertEquals( $attachment_id, $result['post_thumbnail']['attachment_id'] );
@@ -144,7 +144,7 @@ function test_post_thumbnail() {
 		// edit the post without supplying a post_thumbnail and check that it didn't change
 		$post3 = array( 'post_content' => 'Updated post' );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'author', 'author', $post_id, $post3 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $attachment_id, get_post_meta( $post_id, '_thumbnail_id', true ) );
 
 		// create another attachment
@@ -153,19 +153,19 @@ function test_post_thumbnail() {
 		// change the post's post_thumbnail
 		$post4 = array( 'post_thumbnail' => $attachment2_id );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'author', 'author', $post_id, $post4 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $attachment2_id, get_post_meta( $post_id, '_thumbnail_id', true ) );
 
 		// unset the post's post_thumbnail
 		$post5 = array( 'post_thumbnail' => '' );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'author', 'author', $post_id, $post5 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( '', get_post_meta( $post_id, '_thumbnail_id', true ) );
 
 		// use invalid ID
 		$post6 = array( 'post_thumbnail' => 398420983409 );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'author', 'author', $post_id, $post6 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 
 		remove_theme_support( 'post-thumbnails' );
@@ -191,7 +191,7 @@ function test_edit_custom_fields() {
 		);
 
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'contributor', 'contributor', $post_id, $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue($result);
 
 		$out = get_post( $post_id );
@@ -213,7 +213,7 @@ function test_capable_unsticky() {
 
 		$post2 = array( 'sticky' => false );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertFalse( is_sticky( $post_id ) );
 	}
 
@@ -225,7 +225,7 @@ function test_password_transition_unsticky() {
 
 		$post2 = array( 'post_password' => 'foobar',  'sticky' => false );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $post2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertFalse( is_sticky( $post_id ) );
 	}
 
@@ -245,7 +245,7 @@ function test_if_not_modified_since() {
 		// Modify the day old post. In this case, we think it was last modified yesterday.
 		$struct = array( 'post_content' => 'First edit', 'if_not_modified_since' => new IXR_Date( $yesterday ) );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $struct ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Make sure the edit went through.
 		$this->assertEquals( 'First edit', get_post( $post_id )->post_content );
@@ -253,7 +253,7 @@ function test_if_not_modified_since() {
 		// Modify it again. We think it was last modified yesterday, but we actually just modified it above.
 		$struct = array( 'post_content' => 'Second edit', 'if_not_modified_since' => new IXR_Date( $yesterday ) );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $struct ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 409, $result->code );
 
 		// Make sure the edit did not go through.
@@ -273,7 +273,7 @@ function test_edit_attachment() {
 
 		$struct = array( 'post_content' => 'First edit' );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $struct ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Make sure that the post status is still inherit
 		$this->assertEquals( 'inherit', get_post( $post_id )->post_status );
@@ -290,7 +290,7 @@ function test_use_invalid_post_status() {
 
 		$struct = array( 'post_status' => 'doesnt_exists' );
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $struct ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Make sure that the post status is still inherit
 		$this->assertEquals( 'draft', get_post( $post_id )->post_status );
@@ -309,7 +309,7 @@ function test_loss_of_categories_on_edit() {
 		$this->assertContains( $term_id, $term_ids );
 
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, array( 'ID' => $post_id, 'post_title' => 'Updated' ) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( 'Updated', get_post( $post_id )->post_title );
 
 		$term_ids = wp_list_pluck( get_the_category( $post_id ), 'term_id' );
@@ -336,7 +336,7 @@ function test_clear_categories_on_edit() {
 			)
 		);
 		$result = $this->myxmlrpcserver->wp_editPost( array( 1, 'editor', 'editor', $post_id, $new_post_content ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( 'Updated', get_post( $post_id )->post_title );
 
 		$term_ids = wp_list_pluck( get_the_category( $post_id ), 'term_id' );
diff --git a/tests/phpunit/tests/xmlrpc/wp/editProfile.php b/tests/phpunit/tests/xmlrpc/wp/editProfile.php
index 525f6a5..6e3ced5 100644
--- a/tests/phpunit/tests/xmlrpc/wp/editProfile.php
+++ b/tests/phpunit/tests/xmlrpc/wp/editProfile.php
@@ -8,7 +8,7 @@ class Tests_XMLRPC_wp_editProfile extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_editProfile( array( 1, 'username', 'password', array() ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -25,7 +25,7 @@ function test_subscriber_profile() {
             'bio' => rand_str(200)
         );
         $result = $this->myxmlrpcserver->wp_editProfile( array( 1, 'subscriber', 'subscriber', $new_data ) );
-        $this->assertNotInstanceOf( 'IXR_Error', $result );
+        $this->assertNotIXRError( $result );
         $this->assertTrue( $result );
 
         // verify that the new values were stored
@@ -45,7 +45,7 @@ function test_ignore_password_change() {
         $new_data = array( 'password' => $new_pass );
 
         $result = $this->myxmlrpcserver->wp_editProfile( array( 1, 'author', 'author', $new_data ) );
-        $this->assertNotInstanceOf( 'IXR_Error', $result );
+        $this->assertNotIXRError( $result );
         $this->assertTrue( $result );
 
         $auth_old = wp_authenticate( 'author', 'author' );
@@ -60,7 +60,7 @@ function test_ignore_email_change() {
         $new_data = array( 'email' => $new_email );
 
         $result = $this->myxmlrpcserver->wp_editProfile( array( 1, 'editor', 'editor', $new_data ) );
-        $this->assertNotInstanceOf( 'IXR_Error', $result );
+        $this->assertNotIXRError( $result );
         $this->assertTrue( $result );
 
         $user_data = get_userdata( $editor_id );
diff --git a/tests/phpunit/tests/xmlrpc/wp/editTerm.php b/tests/phpunit/tests/xmlrpc/wp/editTerm.php
index b3f22d2..1e62de9 100644
--- a/tests/phpunit/tests/xmlrpc/wp/editTerm.php
+++ b/tests/phpunit/tests/xmlrpc/wp/editTerm.php
@@ -22,7 +22,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'username', 'password', 'category', 1 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -30,7 +30,7 @@ function test_empty_taxonomy() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'subscriber', 'subscriber', '', array( 'taxonomy' => '' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -39,7 +39,7 @@ function test_invalid_taxonomy() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'subscriber', 'subscriber', self::$parent_term, array( 'taxonomy' => 'not_existing' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -48,7 +48,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'subscriber', 'subscriber', self::$parent_term, array( 'taxonomy' => 'category' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 		$this->assertEquals( __( 'Sorry, you are not allowed to edit this term.' ), $result->message );
 	}
@@ -57,7 +57,7 @@ function test_term_not_exists() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', 9999, array( 'taxonomy' => 'category' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 		$this->assertEquals(  __( 'Invalid term ID.' ), $result->message );
 	}
@@ -66,7 +66,7 @@ function test_empty_term() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', '', array( 'taxonomy' => 'category' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 		$this->assertEquals( __('Empty Term'), $result->message );
 	}
@@ -75,7 +75,7 @@ function test_empty_term_name() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', self::$parent_term, array( 'taxonomy' => 'category', 'name' => '' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'The term name cannot be empty.' ), $result->message );
 	}
@@ -84,7 +84,7 @@ function test_parent_for_nonhierarchical() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', self::$post_tag, array( 'taxonomy' => 'post_tag', 'parent' => self::$parent_term ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( "This taxonomy is not hierarchical so you can't set a parent." ), $result->message );
 	}
@@ -93,7 +93,7 @@ function test_parent_empty() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', self::$child_term, array( 'taxonomy' => 'category', 'parent' => '', 'name' => 'test' ) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue( $result );
 	}
 
@@ -102,7 +102,7 @@ function test_parent_null() {
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', self::$child_term, array( 'taxonomy' => 'category', 'parent' => NULL, 'name' => 'test' ) ) );
 
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertInternalType( 'boolean', $result );
 
 		$term = get_term( self::$child_term, 'category' );
@@ -113,7 +113,7 @@ function test_parent_invalid() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', self::$child_term, array( 'taxonomy' => 'category', 'parent' => 'dasda', 'name' => 'test' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 	}
 
@@ -121,7 +121,7 @@ function test_parent_not_existing() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', self::$child_term, array( 'taxonomy' => 'category', 'parent' => 9999, 'name' => 'test' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Parent term does not exist.' ), $result->message );
 	}
@@ -131,7 +131,7 @@ function test_parent_duplicate_slug() {
 
 		$parent_term = get_term_by( 'id', self::$parent_term, 'category' );
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', self::$child_term, array( 'taxonomy' => 'category', 'slug' => $parent_term->slug ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 		$this->assertEquals( htmlspecialchars( sprintf( __('The slug &#8220;%s&#8221; is already in use by another term'), $parent_term->slug ) ), $result->message );
 	}
@@ -142,7 +142,7 @@ function test_edit_all_fields() {
 		$fields = array( 'taxonomy' => 'category', 'name' => 'Child 2', 'parent' => self::$parent_term, 'description' => 'Child term', 'slug' => 'child_2' );
 		$result = $this->myxmlrpcserver->wp_editTerm( array( 1, 'editor', 'editor', self::$child_term, $fields ) );
 
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertInternalType( 'boolean', $result );
 	}
 }
diff --git a/tests/phpunit/tests/xmlrpc/wp/getComment.php b/tests/phpunit/tests/xmlrpc/wp/getComment.php
index d66ccd8..57ad899 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getComment.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getComment.php
@@ -35,7 +35,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getComment( array( 1, 'username', 'password', self::$parent_comment_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -43,7 +43,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'contributor' );
 
 		$result = $this->myxmlrpcserver->wp_getComment( array( 1, 'contributor', 'contributor', self::$parent_comment_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -51,7 +51,7 @@ function test_valid_comment() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getComment( array( 1, 'editor', 'editor', self::$parent_comment_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Check data types
 		$this->assertInternalType( 'string', $result['user_id'] );
@@ -87,7 +87,7 @@ function test_valid_child_comment() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getComment( array( 1, 'editor', 'editor', self::$child_comment_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		$this->assertEquals( self::$child_comment_id, $result['comment_id'] );
 		$this->assertEquals( self::$parent_comment_id, $result['parent'] );
@@ -97,7 +97,7 @@ function test_invalid_id() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getComment( array( 1, 'editor', 'editor', 123456789 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 	}
 }
\ No newline at end of file
diff --git a/tests/phpunit/tests/xmlrpc/wp/getComments.php b/tests/phpunit/tests/xmlrpc/wp/getComments.php
index a4bdf22..49b7253 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getComments.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getComments.php
@@ -8,7 +8,7 @@ class Tests_XMLRPC_wp_getComments extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getComments( array( 1, 'username', 'password', array() ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -16,7 +16,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'contributor' );
 
 		$result = $this->myxmlrpcserver->wp_getComments( array( 1, 'contributor', 'contributor', array() ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -27,7 +27,7 @@ function test_capable_user() {
 		$this->make_user_by_role( 'editor' );
 
 		$results = $this->myxmlrpcserver->wp_getComments( array( 1, 'editor', 'editor', array() ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $result ) {
 			$comment = get_comment( $result['comment_id'], ARRAY_A );
@@ -44,7 +44,7 @@ function test_post_filter() {
 		$results = $this->myxmlrpcserver->wp_getComments( array( 1, 'editor', 'editor', array(
 			'post_id' => $this->post_id
 		) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $result ) {
 			$this->assertEquals( $this->post_id, $result['post_id'] );
@@ -60,7 +60,7 @@ function test_number_filter() {
 		$results = $this->myxmlrpcserver->wp_getComments( array( 1, 'editor', 'editor', array(
 			'post_id' => $this->post_id,
 		) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		// if no 'number' filter is specified, default should be 10
 		$this->assertCount( 10, $results );
@@ -69,7 +69,7 @@ function test_number_filter() {
 			'post_id' => $this->post_id,
 			'number' => 5
 		) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results2 );
+		$this->assertNotIXRError( $results2 );
 		$this->assertCount( 5, $results2 );
 	}
 
@@ -104,7 +104,7 @@ function test_contributor_capabilities() {
 		) );
 
 		$result = $this->myxmlrpcserver->wp_getComments( array( 1, 'contributor', 'contributor' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -140,7 +140,7 @@ function test_author_capabilities() {
 		$result1 = $this->myxmlrpcserver->wp_getComments( array( 1, 'author', 'author', array(
 			'post_id' => $author_post_id
 		) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result1 );
+		$this->assertIXRError( $result1 );
 
 		$result2 = $this->myxmlrpcserver->wp_getComments( array( 1, 'author', 'author', array(
 			'status' => 'approve',
@@ -153,7 +153,7 @@ function test_author_capabilities() {
 		$result3 = $this->myxmlrpcserver->wp_getComments( array( 1, 'author', 'author', array(
 			'post_id' => $editor_post_id
 		) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result3 );
+		$this->assertIXRError( $result3 );
 
 		$result4 = $this->myxmlrpcserver->wp_getComments( array( 1, 'author', 'author', array(
 			'status' => 'approve',
diff --git a/tests/phpunit/tests/xmlrpc/wp/getMediaItem.php b/tests/phpunit/tests/xmlrpc/wp/getMediaItem.php
index 7b99dd5..a091f1c 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getMediaItem.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getMediaItem.php
@@ -38,7 +38,7 @@ function tearDown() {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getMediaItem( array( 1, 'username', 'password', 0 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -47,7 +47,7 @@ function test_valid_media_item() {
 
 		$fields = array( 'post' );
 		$result = $this->myxmlrpcserver->wp_getMediaItem( array( 1, 'author', 'author', $this->attachment_id, $fields ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Check data types
 		$this->assertInternalType( 'string', $result['attachment_id'] );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getOptions.php b/tests/phpunit/tests/xmlrpc/wp/getOptions.php
index 8b095f6..0dffcff 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getOptions.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getOptions.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_getOptions extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getOptions( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
diff --git a/tests/phpunit/tests/xmlrpc/wp/getPage.php b/tests/phpunit/tests/xmlrpc/wp/getPage.php
index 0f377b1..eb57239 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getPage.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getPage.php
@@ -20,7 +20,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getPage( array( 1, self::$post_id, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -31,7 +31,7 @@ function test_invalid_pageid() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPage( array( 1, 9999, 'editor', 'editor' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 	}
 
@@ -39,7 +39,7 @@ function test_valid_page() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPage( array( 1, self::$post_id, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Check data types
 		$this->assertInternalType( 'string', $result['userid'] );
@@ -80,7 +80,7 @@ function test_date() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPage( array( 1, self::$post_id, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		$this->assertInstanceOf( 'IXR_Date', $result['dateCreated'] );
 		$this->assertInstanceOf( 'IXR_Date', $result['date_created_gmt'] );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getPageList.php b/tests/phpunit/tests/xmlrpc/wp/getPageList.php
index ef8f793..0e924c4 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getPageList.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getPageList.php
@@ -20,7 +20,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getPageList( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -28,7 +28,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'contributor' );
 
 		$result = $this->myxmlrpcserver->wp_getPageList( array( 1, 'contributor', 'contributor' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -36,7 +36,7 @@ function test_date() {
 		$this->make_user_by_role( 'editor' );
 
 		$results = $this->myxmlrpcserver->wp_getPageList( array( 1, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $result ) {
 			$page = get_post( $result->page_id );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getPages.php b/tests/phpunit/tests/xmlrpc/wp/getPages.php
index 4242387..f528d83 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getPages.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getPages.php
@@ -26,7 +26,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getPages( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -34,13 +34,13 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'contributor' );
 
 		$result = $this->myxmlrpcserver->wp_getPages( array( 1, 'contributor', 'contributor' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
 	function test_capable_user() {
 		$results = $this->myxmlrpcserver->wp_getPages( array( 1, 'administrator', 'administrator' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $result ) {
 			$page = get_post( $result['page_id'] );
@@ -65,12 +65,12 @@ function test_semi_capable_user() {
 		add_filter( 'map_meta_cap', array( $this, 'remove_editor_edit_page_cap') , 10, 4 );
 
 		$results = $this->myxmlrpcserver->wp_getPages( array( 1, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		$found_incapable = false;
 		foreach( $results as $result ) {
 			// WP#20629
-			$this->assertNotInstanceOf( 'IXR_Error', $result );
+			$this->assertNotIXRError( $result );
 
 			if ( $result['page_id'] == self::$post_id ) {
 				$found_incapable = true;
diff --git a/tests/phpunit/tests/xmlrpc/wp/getPost.php b/tests/phpunit/tests/xmlrpc/wp/getPost.php
index 6e6ecae..b33f8a3 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getPost.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getPost.php
@@ -27,7 +27,7 @@ function setUp() {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getPost( array( 1, 'username', 'password', 1 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -36,7 +36,7 @@ function test_valid_post() {
 
 		$fields = array( 'post', 'custom_fields' );
 		$result = $this->myxmlrpcserver->wp_getPost( array( 1, 'author', 'author', $this->post_id, $fields ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Check data types
 		$this->assertInternalType( 'string', $result['post_id'] );
@@ -79,7 +79,7 @@ function test_valid_post() {
 	function test_no_fields() {
 		$fields = array();
 		$result = $this->myxmlrpcserver->wp_getPost( array( 1, 'author', 'author', $this->post_id, $fields ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// when no fields are requested, only the IDs should be returned
 		$this->assertEquals( 1, count( $result ) );
@@ -88,7 +88,7 @@ function test_no_fields() {
 
 	function test_default_fields() {
 		$result = $this->myxmlrpcserver->wp_getPost( array( 1, 'author', 'author', $this->post_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		$this->assertArrayHasKey( 'post_id', $result );
 		$this->assertArrayHasKey( 'link', $result ); // random field from 'posts' group
@@ -99,7 +99,7 @@ function test_default_fields() {
 	function test_date() {
 		$fields = array( 'post' );
 		$result = $this->myxmlrpcserver->wp_getPost( array( 1, 'author', 'author', $this->post_id, $fields ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		$this->assertInstanceOf( 'IXR_Date', $result['post_date'] );
 		$this->assertInstanceOf( 'IXR_Date', $result['post_date_gmt'] );
@@ -130,7 +130,7 @@ function test_valid_page() {
 		) );
 
 		$result = $this->myxmlrpcserver->wp_getPost( array( 1, 'editor', 'editor', $child_page_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		$this->assertInternalType( 'string', $result['post_id'] );
 		$this->assertInternalType( 'string', $result['post_parent'] );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getPostType.php b/tests/phpunit/tests/xmlrpc/wp/getPostType.php
index a00dbda..cef0fd6 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getPostType.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getPostType.php
@@ -31,7 +31,7 @@ function tearDown() {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getPostType( array( 1, 'username', 'password', 'post' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -39,7 +39,7 @@ function test_invalid_post_type_name() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPostType( array( 1, 'editor', 'editor', 'foobar' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -47,14 +47,14 @@ function test_valid_post_type_name() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPostType( array( 1, 'editor', 'editor', 'post' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_getPostType( array( 1, 'subscriber', 'subscriber', 'post' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -62,7 +62,7 @@ function test_valid_type() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPostType( array( 1, 'editor', 'editor', $this->cpt_name, array( 'labels', 'cap', 'menu', 'taxonomies' ) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// check data types
 		$this->assertInternalType( 'string', $result['name'] );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getPostTypes.php b/tests/phpunit/tests/xmlrpc/wp/getPostTypes.php
index ae78a5d..7188411 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getPostTypes.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getPostTypes.php
@@ -6,7 +6,7 @@
 class Tests_XMLRPC_wp_getPostTypes extends WP_XMLRPC_UnitTestCase {
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getPostTypes( array( 1, 'username', 'password', 'post' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -14,7 +14,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_getPostTypes( array( 1, 'subscriber', 'subscriber' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertInternalType( 'array', $result );
 		$this->assertEquals( 0, count( $result ) );
 	}
@@ -23,7 +23,7 @@ function test_capable_user() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPostTypes( array( 1, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertInternalType( 'array', $result );
 		$this->assertGreaterThan( 0, count( $result ) );
 	}
@@ -32,7 +32,7 @@ function test_simple_filter() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPostTypes( array( 1, 'editor', 'editor', array( 'hierarchical' => true ) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertInternalType( 'array', $result );
 
 		// verify that pages is in the result, and post is not
diff --git a/tests/phpunit/tests/xmlrpc/wp/getPosts.php b/tests/phpunit/tests/xmlrpc/wp/getPosts.php
index 3119fea..28d3c4b 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getPosts.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getPosts.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_getPosts extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getPosts( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -18,12 +18,12 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_getPosts( array( 1, 'subscriber', 'subscriber' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 
 		$filter = array( 'post_type' => 'page' );
 		$result = $this->myxmlrpcserver->wp_getPosts( array( 1, 'subscriber', 'subscriber', $filter ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -31,7 +31,7 @@ function test_capable_user() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_invalid_post_type() {
@@ -39,7 +39,7 @@ function test_invalid_post_type() {
 
 		$filter = array( 'post_type' => 'invalid_post_type_name' );
 		$result = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor', $filter ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 	}
 
 	function test_filters() {
@@ -62,7 +62,7 @@ function test_filters() {
 		// get them all
 		$filter = array( 'post_type' => $cpt_name, 'number' => $num_posts + 10 );
 		$results = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor', $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 		$this->assertEquals( $num_posts, count( $results ) );
 
 		// page through results
@@ -86,7 +86,7 @@ function test_filters() {
 		// get results ordered by comment count
 		$filter2 = array( 'post_type' => $cpt_name, 'number' => $num_posts, 'orderby' => 'comment_count', 'order' => 'DESC' );
 		$results2 = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor', $filter2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results2 );
+		$this->assertNotIXRError( $results2 );
 		$last_comment_count = 100;
 		foreach ( $results2 as $post ) {
 			$comment_count = intval( get_comments_number( $post['post_id'] ) );
@@ -100,7 +100,7 @@ function test_filters() {
 		wp_update_post( $post );
 		$filter3 = array( 'post_type' => $cpt_name, 'post_status' => 'draft' );
 		$results3 = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor', $filter3 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results3 );
+		$this->assertNotIXRError( $results3 );
 		$this->assertEquals( 1, count( $results3 ) );
 		$this->assertEquals( $post->ID, $results3[0]['post_id'] );
 
@@ -113,7 +113,7 @@ function test_fields() {
 
 		// check default fields
 		$results = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 		$expected_fields = array( 'post_id', 'post_title', 'terms', 'custom_fields', 'link' ); // subset of expected fields
 		foreach( $expected_fields as $field ) {
 			$this->assertArrayHasKey( $field, $results[0] );
@@ -123,7 +123,7 @@ function test_fields() {
 		$filter = array();
 		$fields = array( 'post_name', 'post_author', 'enclosure' );
 		$results2 = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor', $filter, $fields ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results2 );
+		$this->assertNotIXRError( $results2 );
 		$expected_fields = array_merge( $fields, array( 'post_id' ) );
 		foreach ( array_keys( $results2[0] ) as $field ) {
 			$this->assertContains( $field, $expected_fields );
@@ -142,13 +142,13 @@ function test_search() {
 		// Search for none of them
 		$filter = array( 's' => 'Third' );
 		$results = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor', $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 		$this->assertEquals( 0, count( $results ) );
 
 		// Search for one of them
 		$filter = array( 's' => 'First:' );
 		$results = $this->myxmlrpcserver->wp_getPosts( array( 1, 'editor', 'editor', $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 		$this->assertEquals( 1, count( $results ) );
 	}
 
diff --git a/tests/phpunit/tests/xmlrpc/wp/getProfile.php b/tests/phpunit/tests/xmlrpc/wp/getProfile.php
index cc2da3e..86fd821 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getProfile.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getProfile.php
@@ -8,7 +8,7 @@ class Tests_XMLRPC_wp_getProfile extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getProfile( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -16,7 +16,7 @@ function test_subscriber() {
 		$subscriber_id = $this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_getProfile( array( 1, 'subscriber', 'subscriber' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $subscriber_id, $result['user_id'] );
 		$this->assertContains( 'subscriber', $result['roles'] );
 	}
@@ -25,7 +25,7 @@ function test_administrator() {
 		$administrator_id = $this->make_user_by_role( 'administrator' );
 
 		$result = $this->myxmlrpcserver->wp_getProfile( array( 1, 'administrator', 'administrator' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $administrator_id, $result['user_id'] );
 		$this->assertContains( 'administrator', $result['roles'] );
 	}
@@ -36,7 +36,7 @@ function test_arbitrary_fields() {
 		$fields = array( 'email', 'bio', 'user_contacts' );
 
 		$result = $this->myxmlrpcserver->wp_getProfile( array( 1, 'editor', 'editor', $fields ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $editor_id, $result['user_id'] );
 
 		$expected_fields = array( 'user_id', 'email', 'bio' );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getRevisions.php b/tests/phpunit/tests/xmlrpc/wp/getRevisions.php
index a407c65..d1aad1e 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getRevisions.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getRevisions.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_getRevisions extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getRevisions( array( 1, 'username', 'password', 0 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -17,7 +17,7 @@ function test_incapable_user() {
 		$post_id = self::factory()->post->create();
 
 		$result = $this->myxmlrpcserver->wp_getRevisions( array( 1, 'subscriber', 'subscriber', $post_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -26,7 +26,7 @@ function test_capable_user() {
 
 		$post_id = self::factory()->post->create();
 		$result = $this->myxmlrpcserver->wp_getRevisions( array( 1, 'editor', 'editor', $post_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_revision_count() {
diff --git a/tests/phpunit/tests/xmlrpc/wp/getTaxonomies.php b/tests/phpunit/tests/xmlrpc/wp/getTaxonomies.php
index cd2c86e..97bd9cf 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getTaxonomies.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getTaxonomies.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_getTaxonomies extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getTaxonomies( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -15,6 +15,6 @@ function test_taxonomy_validated() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTaxonomies( array( 1, 'editor', 'editor' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 }
\ No newline at end of file
diff --git a/tests/phpunit/tests/xmlrpc/wp/getTaxonomy.php b/tests/phpunit/tests/xmlrpc/wp/getTaxonomy.php
index f8446bc..5ffff80 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getTaxonomy.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getTaxonomy.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_getTaxonomy extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getTaxonomy( array( 1, 'username', 'password', 'category' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -15,7 +15,7 @@ function test_empty_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTaxonomy( array( 1, 'editor', 'editor', '' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -24,7 +24,7 @@ function test_invalid_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTaxonomy( array( 1, 'editor', 'editor', 'not_existing' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -33,7 +33,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_getTaxonomy( array( 1, 'subscriber', 'subscriber', 'category' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 		$this->assertEquals( __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ), $result->message );
 	}
@@ -42,14 +42,14 @@ function test_taxonomy_validated() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTaxonomy( array( 1, 'editor', 'editor', 'category' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_prepare_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTaxonomy( array( 1, 'editor', 'editor', 'category' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$taxonomy = get_taxonomy( 'category' );
 		$this->assertEquals( 'category', $result['name'], 'name' );
 		$this->assertEquals( true, $result['_builtin'], '_builtin' );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getTerm.php b/tests/phpunit/tests/xmlrpc/wp/getTerm.php
index 2edbaeb..fca71a7 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getTerm.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getTerm.php
@@ -15,7 +15,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getTerm( array( 1, 'username', 'password', 'category', 1 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -23,7 +23,7 @@ function test_empty_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTerm( array( 1, 'editor', 'editor', '', 0 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -32,7 +32,7 @@ function test_invalid_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTerm( array( 1, 'editor', 'editor', 'not_existing', 0 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -41,7 +41,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_getTerm( array( 1, 'subscriber', 'subscriber', 'category', self::$term_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 		$this->assertEquals( __( 'Sorry, you are not allowed to assign this term.' ), $result->message );
 	}
@@ -51,7 +51,7 @@ function test_empty_term() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTerm( array( 1, 'editor', 'editor', 'category', '' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 		$this->assertEquals( __('Empty Term'), $result->message );
 	}
@@ -60,7 +60,7 @@ function test_invalid_term() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTerm( array( 1, 'editor', 'editor', 'category', 9999 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 		$this->assertEquals( __( 'Invalid term ID.' ), $result->message );
 	}
@@ -72,7 +72,7 @@ function test_valid_term() {
 
 		$result = $this->myxmlrpcserver->wp_getTerm( array( 1, 'editor', 'editor', 'category', self::$term_id ) );
 
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $result, $term );
 
 		// Check DataTypes
diff --git a/tests/phpunit/tests/xmlrpc/wp/getTerms.php b/tests/phpunit/tests/xmlrpc/wp/getTerms.php
index 1673975..b67cbde 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getTerms.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getTerms.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_getTerms extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getTerms( array( 1, 'username', 'password', 'category' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -15,7 +15,7 @@ function test_empty_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', '' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -24,7 +24,7 @@ function test_invalid_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', 'not_existing' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -33,7 +33,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_getTerms( array( 1, 'subscriber', 'subscriber', 'category' ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 		$this->assertEquals( __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ), $result->message );
 	}
@@ -45,7 +45,7 @@ function test_valid_terms() {
 		$cat = wp_insert_term( 'term_' . __FUNCTION__ , 'category' );
 
 		$results = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', 'category' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		foreach( $results as $term ) {
 			$this->assertInternalType( 'int', $term['count'] );
@@ -71,7 +71,7 @@ function test_custom_taxonomy() {
 
 		// test fetching all terms
 		$results = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', $tax_name ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		$this->assertEquals( $num_terms, count( $results ) );
 		foreach ( $results as $term ) {
@@ -81,20 +81,20 @@ function test_custom_taxonomy() {
 		// test paged results
 		$filter = array( 'number' => 5 );
 		$results2 = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', $tax_name, $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 		$this->assertEquals( 5, count( $results2 ) );
 		$this->assertEquals( $results[1]['term_id'], $results2[1]['term_id'] ); // check one of the terms
 
 		$filter['offset'] = 10;
 		$results3 = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', $tax_name, $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results3 );
+		$this->assertNotIXRError( $results3 );
 		$this->assertEquals( $num_terms - 10, count( $results3 ) );
 		$this->assertEquals( $results[11]['term_id'], $results3[1]['term_id'] );
 
 		// test hide_empty (since none have been attached to posts yet, all should be hidden
 		$filter = array( 'hide_empty' => true );
 		$results4 = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', $tax_name, $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results4 );
+		$this->assertNotIXRError( $results4 );
 		$this->assertEquals( 0, count( $results4 ) );
 
 		unset($GLOBALS['wp_taxonomies'][$tax_name]);
@@ -111,7 +111,7 @@ function test_term_ordering() {
 
 		$filter = array( 'orderby' => 'count', 'order' => 'DESC' );
 		$results = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', 'category', $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 		$this->assertNotEquals( 0, count( $results ) );
 
 		foreach( $results as $term ) {
@@ -133,7 +133,7 @@ function test_terms_search() {
 		// search by full name
 		$filter = array( 'search' => $name );
 		$results = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', 'category', $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 		$this->assertEquals( 1, count( $results ) );
 		$this->assertEquals( $name, $results[0]['name'] );
 		$this->assertEquals( $name_id, $results[0]['term_id'] );
@@ -141,7 +141,7 @@ function test_terms_search() {
 		// search by partial name
 		$filter = array( 'search' => substr( $name, 0, 10 ) );
 		$results2 = $this->myxmlrpcserver->wp_getTerms( array( 1, 'editor', 'editor', 'category', $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results2 );
+		$this->assertNotIXRError( $results2 );
 		$this->assertEquals( 1, count( $results2 ) );
 		$this->assertEquals( $name, $results2[0]['name'] );
 		$this->assertEquals( $name_id, $results2[0]['term_id'] );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getUser.php b/tests/phpunit/tests/xmlrpc/wp/getUser.php
index acef3c6..beda930 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getUser.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getUser.php
@@ -25,13 +25,13 @@ function tearDown() {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_getUser( array( 1, 'username', 'password', 1 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
 	function test_invalid_user() {
 		$result = $this->myxmlrpcserver->wp_getUser( array( 1, 'administrator', 'administrator', 34902348908234 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 	}
 
@@ -40,7 +40,7 @@ function test_incapable_user() {
 		$editor_id = $this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getUser( array( 1, 'subscriber', 'subscriber', $editor_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -48,7 +48,7 @@ function test_subscriber_self() {
 		$subscriber_id = $this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_getUser( array( 1, 'subscriber', 'subscriber', $subscriber_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $subscriber_id, $result['user_id'] );
 	}
 
@@ -72,7 +72,7 @@ function test_valid_user() {
 		$user_id = wp_insert_user( $user_data );
 
 		$result = $this->myxmlrpcserver->wp_getUser( array( 1, 'administrator', 'administrator', $user_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// check data types
 		$this->assertInternalType( 'string', $result['user_id'] );
@@ -111,7 +111,7 @@ function test_no_fields() {
 		$editor_id = $this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getUser( array( 1, 'administrator', 'administrator', $editor_id, array() ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $editor_id, $result['user_id'] );
 
 		$expected_fields = array( 'user_id' );
@@ -122,7 +122,7 @@ function test_basic_fields() {
 		$editor_id = $this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_getUser( array( 1, 'administrator', 'administrator', $editor_id, array( 'basic' ) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $editor_id, $result['user_id'] );
 
 		$expected_fields = array( 'user_id', 'username', 'email', 'registered', 'display_name', 'nicename' );
@@ -138,7 +138,7 @@ function test_arbitrary_fields() {
 		$fields = array( 'email', 'bio', 'user_contacts' );
 
 		$result = $this->myxmlrpcserver->wp_getUser( array( 1, 'administrator', 'administrator', $editor_id, $fields ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $editor_id, $result['user_id'] );
 
 		$expected_fields = array( 'user_id', 'email', 'bio' );
diff --git a/tests/phpunit/tests/xmlrpc/wp/getUsers.php b/tests/phpunit/tests/xmlrpc/wp/getUsers.php
index 1860fd1..62169e0 100644
--- a/tests/phpunit/tests/xmlrpc/wp/getUsers.php
+++ b/tests/phpunit/tests/xmlrpc/wp/getUsers.php
@@ -8,7 +8,7 @@ class Tests_XMLRPC_wp_getUsers extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$results = $this->myxmlrpcserver->wp_getUsers( array( 1, 'username', 'password' ) );
-		$this->assertInstanceOf( 'IXR_Error', $results );
+		$this->assertIXRError( $results );
 		$this->assertEquals( 403, $results->code );
 	}
 
@@ -16,7 +16,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$results = $this->myxmlrpcserver->wp_getUsers( array( 1, 'subscriber', 'subscriber' ) );
-		$this->assertInstanceOf( 'IXR_Error', $results );
+		$this->assertIXRError( $results );
 		$this->assertEquals( 401, $results->code );
 	}
 
@@ -24,7 +24,7 @@ function test_capable_user() {
 		$this->make_user_by_role( 'administrator' );
 
 		$result = $this->myxmlrpcserver->wp_getUsers( array( 1, 'administrator', 'administrator' ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// check data types
 		$this->assertInternalType( 'string', $result[0]['user_id'] );
@@ -49,7 +49,7 @@ function test_invalid_role() {
 
 		$filter = array( 'role' => 'invalidrole' );
 		$results = $this->myxmlrpcserver->wp_getUsers( array( 1, 'administrator', 'administrator', $filter ) );
-		$this->assertInstanceOf( 'IXR_Error', $results );
+		$this->assertIXRError( $results );
 		$this->assertEquals( 403, $results->code );
 	}
 
@@ -63,14 +63,14 @@ function test_role_filter() {
 		// test a single role ('editor')
 		$filter = array( 'role' => 'editor' );
 		$results = $this->myxmlrpcserver->wp_getUsers( array( 1, 'administrator', 'administrator', $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 		$this->assertCount( 1, $results );
 		$this->assertEquals( $editor_id, $results[0]['user_id'] );
 
 		// test 'authors', which should return all non-subscribers
 		$filter2 = array( 'who' => 'authors' );
 		$results2 = $this->myxmlrpcserver->wp_getUsers( array( 1, 'administrator', 'administrator', $filter2 ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results2 );
+		$this->assertNotIXRError( $results2 );
 		$this->assertCount( 3, array_intersect( array( $author_id, $editor_id, $administrator_id ), wp_list_pluck( $results2, 'user_id' ) ) );
 	}
 
@@ -104,7 +104,7 @@ function test_order_filters() {
 
 		$filter = array( 'orderby' => 'email', 'order' => 'ASC' );
 		$results = $this->myxmlrpcserver->wp_getUsers( array( 1, 'administrator', 'administrator', $filter ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $results );
+		$this->assertNotIXRError( $results );
 
 		$last_email = '';
 		foreach ( $results as $user ) {
diff --git a/tests/phpunit/tests/xmlrpc/wp/newComment.php b/tests/phpunit/tests/xmlrpc/wp/newComment.php
index 80e70ef..148c6ac 100644
--- a/tests/phpunit/tests/xmlrpc/wp/newComment.php
+++ b/tests/phpunit/tests/xmlrpc/wp/newComment.php
@@ -13,7 +13,7 @@ function test_valid_comment() {
 			'content' => rand_str( 100 )
 		) ) );
  
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
  
 	function test_empty_comment() {
@@ -24,7 +24,7 @@ function test_empty_comment() {
 			'content' => ''
 		) ) );
  
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -40,7 +40,7 @@ function test_new_comment_post_closed() {
 			'content' => rand_str( 100 ),
 		) ) );
 
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -54,12 +54,12 @@ function test_new_comment_duplicated() {
 
 		// First time it's a valid comment
 		$result = $this->myxmlrpcserver->wp_newComment( $comment_args  );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// Run second time for duplication error
 		$result = $this->myxmlrpcserver->wp_newComment( $comment_args );
 
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
diff --git a/tests/phpunit/tests/xmlrpc/wp/newPost.php b/tests/phpunit/tests/xmlrpc/wp/newPost.php
index 3092aef..1b718bc 100644
--- a/tests/phpunit/tests/xmlrpc/wp/newPost.php
+++ b/tests/phpunit/tests/xmlrpc/wp/newPost.php
@@ -7,7 +7,7 @@ class Tests_XMLRPC_wp_newPost extends WP_XMLRPC_UnitTestCase {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'username', 'password', array() ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -15,7 +15,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'subscriber', 'subscriber', array() ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -23,7 +23,7 @@ function test_no_content() {
 		$this->make_user_by_role( 'author' );
 
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'author', 'author', array() ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 		$this->assertEquals( 'Content, title, and excerpt are empty.', $result->message );
 	}
@@ -33,7 +33,7 @@ function test_basic_content() {
 
 		$post = array( 'post_title' => 'Test' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 	}
 
@@ -42,7 +42,7 @@ function test_ignore_id() {
 
 		$post = array( 'post_title' => 'Test', 'ID' => 103948 );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertNotEquals( '103948', $result );
 	}
 
@@ -51,7 +51,7 @@ function test_capable_publish() {
 
 		$post = array( 'post_title' => 'Test', 'post_status' => 'publish' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_incapable_publish() {
@@ -59,7 +59,7 @@ function test_incapable_publish() {
 
 		$post = array( 'post_title' => 'Test', 'post_status' => 'publish' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'contributor', 'contributor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -68,7 +68,7 @@ function test_capable_private() {
 
 		$post = array( 'post_title' => 'Test', 'post_status' => 'private' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_incapable_private() {
@@ -76,7 +76,7 @@ function test_incapable_private() {
 
 		$post = array( 'post_title' => 'Test', 'post_status' => 'private' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'contributor', 'contributor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -86,7 +86,7 @@ function test_capable_other_author() {
 
 		$post = array( 'post_title' => 'Test', 'post_author' => $other_author_id );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_incapable_other_author() {
@@ -95,7 +95,7 @@ function test_incapable_other_author() {
 
 		$post = array( 'post_title' => 'Test', 'post_author' => $other_author_id );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'contributor', 'contributor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -104,7 +104,7 @@ function test_invalid_author() {
 
 		$post = array( 'post_title' => 'Test', 'post_author' => 99999999 );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 404, $result->code );
 	}
 
@@ -113,7 +113,7 @@ function test_empty_author() {
 
 		$post = array( 'post_title' => 'Test' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 
 		$out = get_post( $result );
@@ -132,7 +132,7 @@ function test_post_thumbnail() {
 
 		$post = array( 'post_title' => 'Post Thumbnail Test', 'post_thumbnail' => $attachment_id );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( $attachment_id, get_post_meta( $result, '_thumbnail_id', true ) );
 
 		remove_theme_support( 'post-thumbnails' );
@@ -143,7 +143,7 @@ function test_invalid_post_status() {
 
 		$post = array( 'post_title' => 'Test', 'post_status' => 'foobar_status' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'author', 'author', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( 'draft', get_post_status( $result ) );
 	}
 
@@ -152,7 +152,7 @@ function test_incapable_sticky() {
 
 		$post = array( 'post_title' => 'Test', 'sticky' => true );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'contributor', 'contributor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -161,7 +161,7 @@ function test_capable_sticky() {
 
 		$post = array( 'post_title' => 'Test', 'sticky' => true );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertTrue( is_sticky( $result ) );
 	}
 
@@ -170,7 +170,7 @@ function test_private_sticky() {
 
 		$post = array( 'post_title' => 'Test', 'post_status' => 'private', 'sticky' => true );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -179,7 +179,7 @@ function test_post_format() {
 
 		$post = array( 'post_title' => 'Test', 'post_format' => 'quote' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( 'quote', get_post_format( $result ) );
 	}
 
@@ -188,7 +188,7 @@ function test_invalid_post_format() {
 
 		$post = array( 'post_title' => 'Test', 'post_format' => 'tumblr' );
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertEquals( '', get_post_format( $result ) );
 	}
 
@@ -202,7 +202,7 @@ function test_invalid_taxonomy() {
 			)
 		);
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 
 		$post2 = array(
@@ -212,7 +212,7 @@ function test_invalid_taxonomy() {
 			)
 		);
 		$result2 = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post2 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result2 );
+		$this->assertIXRError( $result2 );
 		$this->assertEquals( 401, $result2->code );
 	}
 
@@ -226,7 +226,7 @@ function test_invalid_term_id() {
 			)
 		);
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -247,7 +247,7 @@ function test_terms() {
 			)
 		);
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		$post_tags = wp_get_object_terms( $result, 'post_tag', array( 'fields' => 'ids' ) );
 		$this->assertNotContains( $tag1['term_id'], $post_tags );
@@ -274,7 +274,7 @@ function test_terms_names() {
 			)
 		);
 		$result = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		// verify that cat2 was created
 		$cat2 = get_term_by( 'name', $cat2_name, 'category' );
 		$this->assertNotEmpty( $cat2 );
@@ -291,7 +291,7 @@ function test_terms_names() {
 			)
 		);
 		$result2 = $this->myxmlrpcserver->wp_newPost( array( 1, 'editor', 'editor', $post2 ) );
-		$this->assertInstanceOf( 'IXR_Error', $result2 );
+		$this->assertIXRError( $result2 );
 		$this->assertEquals( 401, $result2->code );
 	}
 
diff --git a/tests/phpunit/tests/xmlrpc/wp/newTerm.php b/tests/phpunit/tests/xmlrpc/wp/newTerm.php
index 860e5c8..f5f012f 100644
--- a/tests/phpunit/tests/xmlrpc/wp/newTerm.php
+++ b/tests/phpunit/tests/xmlrpc/wp/newTerm.php
@@ -15,7 +15,7 @@ public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'username', 'password', array() ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -23,7 +23,7 @@ function test_empty_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', array( 'taxonomy' => '' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -32,7 +32,7 @@ function test_invalid_taxonomy() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', array( 'taxonomy' => 'not_existing' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Invalid taxonomy.' ), $result->message );
 	}
@@ -41,7 +41,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'subscriber', 'subscriber', array( 'taxonomy' => 'category' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 		$this->assertEquals( __( 'Sorry, you are not allowed to create terms in this taxonomy.' ), $result->message );
 	}
@@ -50,7 +50,7 @@ function test_empty_term() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', array( 'taxonomy' => 'category', 'name' => '' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'The term name cannot be empty.' ), $result->message );
 	}
@@ -59,7 +59,7 @@ function test_parent_for_nonhierarchical() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', array( 'taxonomy' => 'post_tag', 'parent' => self::$parent_term_id, 'name' => 'test' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'This taxonomy is not hierarchical.' ), $result->message );
 	}
@@ -68,7 +68,7 @@ function test_parent_invalid() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', array( 'taxonomy' => 'category', 'parent' => 'dasda', 'name' => 'test' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 500, $result->code );
 	}
 
@@ -76,7 +76,7 @@ function test_parent_not_existing() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', array( 'taxonomy' => 'category', 'parent' => 9999, 'name' => 'test' ) ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 		$this->assertEquals( __( 'Parent term does not exist.' ), $result->message );
 	}
@@ -86,7 +86,7 @@ function test_add_term() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', array( 'taxonomy' => 'category', 'name' => 'test' ) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 	}
 
@@ -94,7 +94,7 @@ function test_add_term_with_parent() {
 		$this->make_user_by_role( 'editor' );
 
 		$result  = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', array( 'taxonomy' => 'category', 'parent' => self::$parent_term_id, 'name' => 'test' ) ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 	}
 
@@ -103,7 +103,7 @@ function test_add_term_with_all() {
 
 		$taxonomy = array( 'taxonomy' => 'category', 'parent' => self::$parent_term_id, 'name' => 'test_all', 'description' => 'Test all', 'slug' => 'test_all' );
 		$result  = $this->myxmlrpcserver->wp_newTerm( array( 1, 'editor', 'editor', $taxonomy ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 		$this->assertStringMatchesFormat( '%d', $result );
 	}
 }
diff --git a/tests/phpunit/tests/xmlrpc/wp/restoreRevision.php b/tests/phpunit/tests/xmlrpc/wp/restoreRevision.php
index 75f5d74..be9d191 100644
--- a/tests/phpunit/tests/xmlrpc/wp/restoreRevision.php
+++ b/tests/phpunit/tests/xmlrpc/wp/restoreRevision.php
@@ -23,7 +23,7 @@ function setUp() {
 
 	function test_invalid_username_password() {
 		$result = $this->myxmlrpcserver->wp_restoreRevision( array( 1, 'username', 'password', $this->revision_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 403, $result->code );
 	}
 
@@ -31,7 +31,7 @@ function test_incapable_user() {
 		$this->make_user_by_role( 'subscriber' );
 
 		$result = $this->myxmlrpcserver->wp_restoreRevision( array( 1, 'subscriber', 'subscriber', $this->revision_id ) );
-		$this->assertInstanceOf( 'IXR_Error', $result );
+		$this->assertIXRError( $result );
 		$this->assertEquals( 401, $result->code );
 	}
 
@@ -39,7 +39,7 @@ function test_capable_user() {
 		$this->make_user_by_role( 'editor' );
 
 		$result = $this->myxmlrpcserver->wp_restoreRevision( array( 1, 'editor', 'editor', $this->revision_id ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 	}
 
 	function test_revision_restored() {
diff --git a/tests/phpunit/tests/xmlrpc/wp/uploadFile.php b/tests/phpunit/tests/xmlrpc/wp/uploadFile.php
index 8baa3f6..e15f85d 100644
--- a/tests/phpunit/tests/xmlrpc/wp/uploadFile.php
+++ b/tests/phpunit/tests/xmlrpc/wp/uploadFile.php
@@ -25,7 +25,7 @@ function test_valid_attachment() {
 
 
 		$result = $this->myxmlrpcserver->mw_newMediaObject( array( 0, 'editor', 'editor', $data ) );
-		$this->assertNotInstanceOf( 'IXR_Error', $result );
+		$this->assertNotIXRError( $result );
 
 		// check data types
 		$this->assertInternalType( 'string', $result['id'] );
