<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[BuddyPress][6905] trunk: Moves automated testing suite into BuddyPress core</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg > ul, #logmsg > ol { margin-left: 0; margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://buddypress.trac.wordpress.org/changeset/6905">6905</a></dd>
<dt>Author</dt> <dd>boonebgorges</dd>
<dt>Date</dt> <dd>2013-04-11 00:00:13 +0000 (Thu, 11 Apr 2013)</dd>
</dl>

<h3>Log Message</h3>
<pre>Moves automated testing suite into BuddyPress core

The new, improved BuddyPress testing suite boasts the following features:
- It uses a vanilla copy of the WordPress test suite as a basis, for maximum
  portability and future-compatibility
- Allows for BP-dependent plugins to write their own tests that use the BP test
  libraries (like our data factories)
- Conforms to current best practices for building WordPress plugin tests

The existing testcases themselves will be ported over in the next changeset, so
as to keep this one reasonably small.

See the Codex page (forthcoming) on how to set up and run the tests, how to add
to them, and how to write BP-dependent tests in your own plugin.

See <a href="http://buddypress.trac.wordpress.org/ticket/4889">#4889</a>

Props djpaul, boonebgorges</pre>

<h3>Added Paths</h3>
<ul>
<li>trunk/tests/</li>
<li><a href="#trunktestsbootstrapphp">trunk/tests/bootstrap.php</a></li>
<li>trunk/tests/includes/</li>
<li><a href="#trunktestsincludesfactoryphp">trunk/tests/includes/factory.php</a></li>
<li><a href="#trunktestsincludesinstallphp">trunk/tests/includes/install.php</a></li>
<li><a href="#trunktestsincludesloaderphp">trunk/tests/includes/loader.php</a></li>
<li><a href="#trunktestsincludestestcasephp">trunk/tests/includes/testcase.php</a></li>
<li><a href="#trunktestsphpunitxml">trunk/tests/phpunit.xml</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunktestsbootstrapphp"></a>
<div class="addfile"><h4>Added: trunk/tests/bootstrap.php (0 => 6905)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/bootstrap.php                                (rev 0)
+++ trunk/tests/bootstrap.php        2013-04-11 00:00:13 UTC (rev 6905)
</span><span class="lines">@@ -0,0 +1,13 @@
</span><ins>+&lt;?php
+
+require_once getenv( 'WP_TESTS_DIR' ) . '/includes/functions.php';
+
+function _install_and_load_buddypress() {
+        require dirname( __FILE__ ) . '/includes/loader.php';
+}
+tests_add_filter( 'muplugins_loaded', '_install_and_load_buddypress' );
+
+require getenv( 'WP_TESTS_DIR' ) . '/includes/bootstrap.php';
+
+// Load the BP-specific testing tools
+require dirname( __FILE__ ) . '/includes/testcase.php';
</ins></span></pre></div>
<a id="trunktestsincludesfactoryphp"></a>
<div class="addfile"><h4>Added: trunk/tests/includes/factory.php (0 => 6905)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/includes/factory.php                                (rev 0)
+++ trunk/tests/includes/factory.php        2013-04-11 00:00:13 UTC (rev 6905)
</span><span class="lines">@@ -0,0 +1,145 @@
</span><ins>+&lt;?php
+class BP_UnitTest_Factory extends WP_UnitTest_Factory {
+        public $activity = null;
+
+        function __construct() {
+                parent::__construct();
+
+                $this-&gt;activity = new BP_UnitTest_Factory_For_Activity( $this );
+                $this-&gt;group = new BP_UnitTest_Factory_For_Group( $this );
+                $this-&gt;xprofile_group = new BP_UnitTest_Factory_For_XProfileGroup( $this );
+                $this-&gt;xprofile_field = new BP_UnitTest_Factory_For_XProfileField( $this );
+        }
+}
+
+class BP_UnitTest_Factory_For_Activity extends WP_UnitTest_Factory_For_Thing {
+
+        function __construct( $factory = null ) {
+                parent::__construct( $factory );
+
+                $this-&gt;default_generation_definitions = array(
+                        'action'       =&gt; new WP_UnitTest_Generator_Sequence( 'Activity action %s' ),
+                        'component'    =&gt; buddypress()-&gt;activity-&gt;id,
+                        'content'      =&gt; new WP_UnitTest_Generator_Sequence( 'Activity content %s' ),
+                        'primary_link' =&gt; 'http://example.com',
+                        'type'         =&gt; 'activity_update',
+                );
+        }
+
+        function create_object( $args ) {
+                if ( ! isset( $args['user_id'] ) )
+                        $args['user_id'] = get_current_user_id();
+
+                return $this-&gt;get_object_by_id( bp_activity_add( $args ) );
+        }
+
+        function update_object( $activity_id, $fields ) {
+                $activity = new BP_Activity_Activity( $activity_id );
+
+                foreach ( $fields as $field_name =&gt; $value ) {
+                        if ( isset( $activity-&gt;$field_name ) )
+                                $activity-&gt;$field_name = $value;
+                }
+
+                $activity-&gt;save();
+                return $activity;
+        }
+
+        function get_object_by_id( $user_id ) {
+                return new BP_Activity_Activity( $user_id );
+        }
+}
+
+class BP_UnitTest_Factory_For_Group extends WP_UnitTest_Factory_For_Thing {
+
+        function __construct( $factory = null ) {
+                parent::__construct( $factory );
+
+                $this-&gt;default_generation_definitions = array(
+                        'name'         =&gt; new WP_UnitTest_Generator_Sequence( 'Group %s' ),
+                        'description'  =&gt; new WP_UnitTest_Generator_Sequence( 'Group description %s' ),
+                        'slug'         =&gt; new WP_UnitTest_Generator_Sequence( 'group-slug-%s' ),
+                        'status'       =&gt; 'public',
+                        'enable_forum' =&gt; true,
+                        'date_created' =&gt; bp_core_current_time(),
+                );
+        }
+
+        function create_object( $args ) {
+                if ( ! isset( $args['creator_id'] ) ) {
+                        $args['creator_id'] = get_current_user_id();
+                }
+
+                $group_id = groups_create_group( $args );
+
+                groups_update_groupmeta( $group_id, 'total_member_count', 1 );
+                groups_update_groupmeta( $group_id, 'last_activity', bp_core_current_time() );
+
+                return $this-&gt;get_object_by_id( $group_id );
+        }
+
+        function update_object( $group_id, $fields ) {
+                $group = new BP_Groups_Group( $group_id );
+
+                foreach ( $fields as $field_name =&gt; $value ) {
+                        if ( isset( $group-&gt;field_name ) )
+                                $group-&gt;field_name = $value;
+                }
+
+                $group-&gt;save();
+                return $group;
+        }
+
+        function get_object_by_id( $group_id ) {
+                return new BP_Groups_Group( $group_id );
+        }
+}
+
+class BP_UnitTest_Factory_For_XProfileGroup extends WP_UnitTest_Factory_For_Thing {
+
+        function __construct( $factory = null ) {
+                parent::__construct( $factory );
+
+                $this-&gt;default_generation_definitions = array(
+                        'name'         =&gt; new WP_UnitTest_Generator_Sequence( 'XProfile group %s' ),
+                        'description'  =&gt; new WP_UnitTest_Generator_Sequence( 'XProfile group description %s' ),
+                        'slug'         =&gt; new WP_UnitTest_Generator_Sequence( 'xprofile-group-slug-%s' ),
+                );
+        }
+
+        function create_object( $args ) {
+                $group_id = xprofile_insert_field_group( $args );
+                return $this-&gt;get_object_by_id( $group_id );
+        }
+
+        function update_object( $group_id, $fields ) {
+        }
+
+        function get_object_by_id( $group_id ) {
+                return new BP_XProfile_Group( $group_id );
+        }
+}
+
+class BP_UnitTest_Factory_For_XProfileField extends WP_UnitTest_Factory_For_Thing {
+
+        function __construct( $factory = null ) {
+                parent::__construct( $factory );
+
+                $this-&gt;default_generation_definitions = array(
+                        'name'         =&gt; new WP_UnitTest_Generator_Sequence( 'XProfile field %s' ),
+                        'description'  =&gt; new WP_UnitTest_Generator_Sequence( 'XProfile field description %s' ),
+                );
+        }
+
+        function create_object( $args ) {
+                $field_id = xprofile_insert_field( $args );
+                return $this-&gt;get_object_by_id( $field_id );
+        }
+
+        function update_object( $field_id, $fields ) {
+        }
+
+        function get_object_by_id( $field_id ) {
+                return new BP_XProfile_Field( $field_id );
+        }
+}
</ins></span></pre></div>
<a id="trunktestsincludesinstallphp"></a>
<div class="addfile"><h4>Added: trunk/tests/includes/install.php (0 => 6905)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/includes/install.php                                (rev 0)
+++ trunk/tests/includes/install.php        2013-04-11 00:00:13 UTC (rev 6905)
</span><span class="lines">@@ -0,0 +1,60 @@
</span><ins>+&lt;?php
+/**
+ * Installs BuddyPress for the purpose of the unit-tests
+ *
+ * @todo Reuse the init/load code in init.php
+ * @todo Support MULTIBLOG
+ */
+error_reporting( E_ALL &amp; ~E_DEPRECATED &amp; ~E_STRICT );
+
+$config_file_path = $argv[1];
+$multisite = ! empty( $argv[2] );
+
+require_once $config_file_path;
+require_once dirname( $config_file_path ) . '/includes/functions.php';
+
+// Set BP to be an active plugin
+$GLOBALS['wp_tests_options'] = array(
+        'active_plugins' =&gt; 'buddypress/bp-loader.php',
+);
+define( 'BP_ROOT_BLOG', 1 );
+
+// Always load admin bar
+tests_add_filter( 'show_admin_bar', '__return_true' );
+
+function wp_tests_options( $value ) {
+        $key = substr( current_filter(), strlen( 'pre_option_' ) );
+        return $GLOBALS['wp_tests_options'][$key];
+}
+foreach ( array_keys( $GLOBALS['wp_tests_options'] ) as $key ) {
+        tests_add_filter( 'pre_option_'.$key, 'wp_tests_options' );
+}
+
+function wp_test_bp_install( $value ) {
+        return array( 'activity' =&gt; 1, 'friends' =&gt; 1, 'groups' =&gt; 1, 'members' =&gt; 1, 'messages' =&gt; 1, 'settings' =&gt; 1, 'xprofile' =&gt; 1, );
+}
+tests_add_filter( 'bp_new_install_default_components', 'wp_test_bp_install' );
+
+$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
+$_SERVER['HTTP_HOST'] = WP_TESTS_DOMAIN;
+$PHP_SELF = $GLOBALS['PHP_SELF'] = $_SERVER['PHP_SELF'] = '/index.php';
+
+require_once ABSPATH . '/wp-settings.php';
+define( 'BP_TESTS_DB_VERSION_FILE', ABSPATH . '.bp-tests-version' );
+
+// Check if BuddyPress has already been installed
+$db_version = bp_get_db_version_raw();
+
+if ( $db_version &amp;&amp; file_exists( BP_TESTS_DB_VERSION_FILE ) ) {
+        $file_version = file_get_contents( BP_TESTS_DB_VERSION_FILE );
+
+        if ( $db_version == (int) $file_version )
+                return;
+}
+
+echo &quot;Installing BuddyPress...\n&quot;;
+
+// Install BuddyPress
+bp_version_updater();
+
+file_put_contents( BP_TESTS_DB_VERSION_FILE, bp_get_db_version_raw() );
</ins></span></pre></div>
<a id="trunktestsincludesloaderphp"></a>
<div class="addfile"><h4>Added: trunk/tests/includes/loader.php (0 => 6905)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/includes/loader.php                                (rev 0)
+++ trunk/tests/includes/loader.php        2013-04-11 00:00:13 UTC (rev 6905)
</span><span class="lines">@@ -0,0 +1,9 @@
</span><ins>+&lt;?php
+
+// Install BP
+$config_file_path = getenv( 'WP_TESTS_DIR' ) . '/wp-tests-config.php';
+$multisite = (int) ( defined( 'WP_TESTS_MULTISITE') &amp;&amp; WP_TESTS_MULTISITE );
+system( WP_PHP_BINARY . ' ' . escapeshellarg( dirname( __FILE__ ) . '/install.php' ) . ' ' . escapeshellarg( $config_file_path ) . ' ' . $multisite );
+
+// Bootstrap BP
+require dirname( __FILE__ ) . '/../../bp-loader.php';
</ins></span></pre></div>
<a id="trunktestsincludestestcasephp"></a>
<div class="addfile"><h4>Added: trunk/tests/includes/testcase.php (0 => 6905)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/includes/testcase.php                                (rev 0)
+++ trunk/tests/includes/testcase.php        2013-04-11 00:00:13 UTC (rev 6905)
</span><span class="lines">@@ -0,0 +1,133 @@
</span><ins>+&lt;?php
+
+/**
+ * WP's test suite wipes out BP's directory page mappings with _delete_all_posts()
+ * We must reestablish them before our tests can be successfully run
+ */
+bp_core_add_page_mappings( bp_get_option( 'bp-active-components' ), 'delete' );
+
+require_once dirname( __FILE__ ) . '/factory.php';
+
+class BP_UnitTestCase extends WP_UnitTestCase {
+
+        public function setUp() {
+                parent::setUp();
+                $this-&gt;factory = new BP_UnitTest_Factory;
+        }
+
+        function clean_up_global_scope() {
+                buddypress()-&gt;bp_nav                = buddypress()-&gt;bp_options_nav = buddypress()-&gt;action_variables = buddypress()-&gt;canonical_stack = buddypress()-&gt;unfiltered_uri = $GLOBALS['bp_unfiltered_uri'] = array();
+                buddypress()-&gt;current_component     = buddypress()-&gt;current_item = buddypress()-&gt;current_action = '';
+                buddypress()-&gt;unfiltered_uri_offset = 0;
+                buddypress()-&gt;is_single_item        = false;
+                buddypress()-&gt;current_user          = new stdClass();
+                buddypress()-&gt;displayed_user        = new stdClass();
+                buddypress()-&gt;loggedin_user         = new stdClass();
+
+                parent::clean_up_global_scope();
+        }
+
+        function assertPreConditions() {
+                parent::assertPreConditions();
+
+                // Reinit some of the globals that might have been cleared by BP_UnitTestCase::clean_up_global_scope().
+                // This is here because it didn't work in clean_up_global_scope(); I don't know why.
+                do_action( 'bp_setup_globals' );
+        }
+
+        function go_to( $url ) {
+                // Set this for bp_core_set_uri_globals()
+                $GLOBALS['_SERVER']['REQUEST_URI'] = $url = str_replace( network_home_url(), '', $url );
+
+                // note: the WP and WP_Query classes like to silently fetch parameters
+                // from all over the place (globals, GET, etc), which makes it tricky
+                // to run them more than once without very carefully clearing everything
+                $_GET = $_POST = array();
+                foreach (array('query_string', 'id', 'postdata', 'authordata', 'day', 'currentmonth', 'page', 'pages', 'multipage', 'more', 'numpages', 'pagenow') as $v) {
+                        if ( isset( $GLOBALS[$v] ) ) unset( $GLOBALS[$v] );
+                }
+                $parts = parse_url($url);
+                if (isset($parts['scheme'])) {
+                        $req = $parts['path'];
+                        if (isset($parts['query'])) {
+                                $req .= '?' . $parts['query'];
+                                // parse the url query vars into $_GET
+                                parse_str($parts['query'], $_GET);
+                        }
+                } else {
+                        $req = $url;
+                }
+                if ( ! isset( $parts['query'] ) ) {
+                        $parts['query'] = '';
+                }
+
+                // Scheme
+                if ( 0 === strpos( $req, '/wp-admin' ) &amp;&amp; force_ssl_admin() ) {
+                        $_SERVER['HTTPS'] = 'on';
+                } else {
+                        unset( $_SERVER['HTTPS'] );
+                }
+
+                $_SERVER['REQUEST_URI'] = $req;
+                unset($_SERVER['PATH_INFO']);
+
+                $this-&gt;flush_cache();
+                unset($GLOBALS['wp_query'], $GLOBALS['wp_the_query']);
+                $GLOBALS['wp_the_query'] =&amp; new WP_Query();
+                $GLOBALS['wp_query'] =&amp; $GLOBALS['wp_the_query'];
+                $GLOBALS['wp'] =&amp; new WP();
+
+                // clean out globals to stop them polluting wp and wp_query
+                foreach ($GLOBALS['wp']-&gt;public_query_vars as $v) {
+                        unset($GLOBALS[$v]);
+                }
+                foreach ($GLOBALS['wp']-&gt;private_query_vars as $v) {
+                        unset($GLOBALS[$v]);
+                }
+
+                $GLOBALS['wp']-&gt;main($parts['query']);
+
+                // For BuddyPress, James.
+                do_action( 'bp_init' );
+        }
+
+        protected function checkRequirements() {
+                if ( WP_TESTS_FORCE_KNOWN_BUGS )
+                        return;
+
+                parent::checkRequirements();
+
+                $tickets = PHPUnit_Util_Test::getTickets( get_class( $this ), $this-&gt;getName( false ) );
+                foreach ( $tickets as $ticket ) {
+                        if ( 'BP' == substr( $ticket, 0, 2 ) ) {
+                                $ticket = substr( $ticket, 2 );
+                                if ( $ticket &amp;&amp; is_numeric( $ticket ) )
+                                        $this-&gt;knownBPBug( $ticket );
+                        }
+                }
+        }
+
+        /**
+         * Skips the current test if there is an open BuddyPress ticket with id $ticket_id
+         */
+        function knownBPBug( $ticket_id ) {
+                if ( WP_TESTS_FORCE_KNOWN_BUGS || in_array( $ticket_id, self::$forced_tickets ) )
+                        return;
+
+                if ( ! TracTickets::isTracTicketClosed( 'http://buddypress.trac.wordpress.org', $ticket_id ) )
+                        $this-&gt;markTestSkipped( sprintf( 'BuddyPress Ticket #%d is not fixed', $ticket_id ) );
+        }
+
+        /**
+         * WP's core tests use wp_set_current_user() to change the current
+         * user during tests. BP caches the current user differently, so we
+         * have to do a bit more work to change it
+         *
+         * @global BuddyPres $bp
+         */
+        function set_current_user( $user_id ) {
+                global $bp;
+                $bp-&gt;loggedin_user-&gt;id = $user_id;
+                wp_set_current_user( $user_id );
+        }
+}
</ins></span></pre></div>
<a id="trunktestsphpunitxml"></a>
<div class="addfile"><h4>Added: trunk/tests/phpunit.xml (0 => 6905)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/tests/phpunit.xml                                (rev 0)
+++ trunk/tests/phpunit.xml        2013-04-11 00:00:13 UTC (rev 6905)
</span><span class="lines">@@ -0,0 +1,14 @@
</span><ins>+&lt;phpunit
+        bootstrap=&quot;bootstrap.php&quot;
+        backupGlobals=&quot;false&quot;
+        colors=&quot;true&quot;
+        convertErrorsToExceptions=&quot;true&quot;
+        convertNoticesToExceptions=&quot;true&quot;
+        convertWarningsToExceptions=&quot;true&quot;
+        &gt;
+        &lt;testsuites&gt;
+                &lt;testsuite&gt;
+                        &lt;directory suffix=&quot;.php&quot;&gt;./testcases/&lt;/directory&gt;
+                &lt;/testsuite&gt;
+        &lt;/testsuites&gt;
+&lt;/phpunit&gt;
</ins></span></pre>
</div>
</div>

</body>
</html>