<!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>[760] trunk: Initial port of nikolayb's phpunit-based framework.</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://unit-tests.trac.wordpress.org/changeset/760">760</a></dd>
<dt>Author</dt> <dd>maxcutler</dd>
<dt>Date</dt> <dd>2012-06-30 17:30:59 +0000 (Sat, 30 Jun 2012)</dd>
</dl>

<h3>Log Message</h3>
<pre>Initial port of nikolayb's phpunit-based framework.

See <a href="http://unit-tests.trac.wordpress.org/ticket/42">#42</a>.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkREADMEtxt">trunk/README.txt</a></li>
<li><a href="#trunkwpconfigsamplephp">trunk/wp-config-sample.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkboostrapphp">trunk/boostrap.php</a></li>
<li><a href="#trunkphpunitxml">trunk/phpunit.xml</a></li>
<li>trunk/wp-testbin/</li>
<li><a href="#trunkwptestbininstallphp">trunk/wp-testbin/install.php</a></li>
<li><a href="#trunkwptestbinmsinstallphp">trunk/wp-testbin/ms-install.php</a></li>
<li><a href="#trunkwptestcasetest_simplephp">trunk/wp-testcase/test_simple.php</a></li>
<li><a href="#trunkwptestlibexceptionsphp">trunk/wp-testlib/exceptions.php</a></li>
<li><a href="#trunkwptestlibfactoryphp">trunk/wp-testlib/factory.php</a></li>
<li><a href="#trunkwptestlibfunctionsphp">trunk/wp-testlib/functions.php</a></li>
<li><a href="#trunkwptestlibtestcasephp">trunk/wp-testlib/testcase.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkREADMEtxt"></a>
<div class="modfile"><h4>Modified: trunk/README.txt (759 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/README.txt        2012-06-30 17:26:55 UTC (rev 759)
+++ trunk/README.txt        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -6,14 +6,14 @@
</span><span class="cx"> 
</span><span class="cx"> 3. $ svn up
</span><span class="cx"> 
</span><del>-4. $ php wp-test.php
</del><ins>+4. $ phpunit path/to/test_case.php
</ins><span class="cx"> 
</span><span class="cx"> Notes:
</span><span class="cx"> 
</span><del>-Test cases live in the 'wp-testcase' subdirectory.  All files in that directory will be included by default.  Extend the WPTestCase class to ensure your test is run.
</del><ins>+Test cases live in the 'wp-testcase' subdirectory.  All files in that directory will be included by default.  Extend the WP_UnitTestCase class to ensure your test is run.
</ins><span class="cx"> 
</span><del>-wp-test.php will initialize and install a (more or less) complete running copy of WordPress each time it is run.  This makes it possible to run functional interface and module tests against a fully working database and codebase, as opposed to pure unit tests with mock objects and stubs.  Pure unit tests may be used also, of course.
</del><ins>+phpunit will initialize and install a (more or less) complete running copy of WordPress each time it is run.  This makes it possible to run functional interface and module tests against a fully working database and codebase, as opposed to pure unit tests with mock objects and stubs.  Pure unit tests may be used also, of course.
</ins><span class="cx"> 
</span><span class="cx"> The test database will be wiped clean with DROP TABLE statements once tests are finished, to ensure a clean start next time the tests are run.
</span><span class="cx"> 
</span><del>-wp-test.php is intended to run at the command line, not via a web server.
</del><ins>+phpunit is intended to run at the command line, not via a web server.
</ins></span></pre></div>
<a id="trunkboostrapphp"></a>
<div class="addfile"><h4>Added: trunk/boostrap.php (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/boostrap.php                                (rev 0)
+++ trunk/boostrap.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,50 @@
</span><ins>+&lt;?php
+/**
+ * Installs WordPress for running the tests and loads WordPress and the test libraries
+ */
+
+error_reporting( E_ALL &amp; ~E_DEPRECATED &amp; ~E_STRICT );
+
+require_once 'PHPUnit/Autoload.php';
+
+$config_file_path = dirname( __FILE__ ) . '/wp-config.php';
+
+/*
+ * Globalize some WordPress variables, because PHPUnit loads this file inside a function
+ * See: https://github.com/sebastianbergmann/phpunit/issues/325
+ *
+ * These are not needed for WordPress 3.3+, only for older versions
+*/
+global $table_prefix, $wp_embed, $wp_locale, $_wp_deprecated_widgets_callbacks, $wp_widget_factory;
+
+// These are still needed
+global $wpdb, $current_site, $current_blog, $wp_rewrite, $shortcode_tags, $wp;
+
+require_once $config_file_path;
+
+$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
+$_SERVER['HTTP_HOST'] = WP_TESTS_DOMAIN;
+$PHP_SELF = $GLOBALS['PHP_SELF'] = $_SERVER['PHP_SELF'] = '/index.php';
+
+system( WP_PHP_BINARY . ' ' . escapeshellarg( dirname( __FILE__ ) . '/wp-testbin/install.php' ) . ' ' . escapeshellarg( $config_file_path ) );
+
+require dirname( __FILE__ ) . '/wp-testlib/functions.php';
+
+// Preset WordPress options defined in bootstrap file.
+// Used to activate themes, plugins, as well as  other settings.
+if(isset($GLOBALS['wp_tests_options'])) {
+        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' );
+        }
+}
+
+// Load WordPress
+require_once ABSPATH . '/wp-settings.php';
+
+require dirname( __FILE__ ) . '/wp-testlib/testcase.php';
+require dirname( __FILE__ ) . '/wp-testlib/exceptions.php';
</ins></span></pre></div>
<a id="trunkphpunitxml"></a>
<div class="addfile"><h4>Added: trunk/phpunit.xml (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/phpunit.xml                                (rev 0)
+++ trunk/phpunit.xml        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,12 @@
</span><ins>+&lt;phpunit
+        bootstrap=&quot;boostrap.php&quot;
+        backupGlobals=&quot;false&quot;
+        colors=&quot;true&quot;
+        &gt;
+    &lt;testsuites&gt;
+        &lt;!-- Default test suite to run all tests --&gt;
+        &lt;testsuite&gt;
+            &lt;directory suffix=&quot;.php&quot;&gt;wp-testcase&lt;/directory&gt;
+        &lt;/testsuite&gt;
+    &lt;/testsuites&gt;
+&lt;/phpunit&gt;
</ins></span></pre></div>
<a id="trunkwpconfigsamplephp"></a>
<div class="modfile"><h4>Modified: trunk/wp-config-sample.php (759 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-config-sample.php        2012-06-30 17:26:55 UTC (rev 759)
+++ trunk/wp-config-sample.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -1,4 +1,8 @@
</span><span class="cx"> &lt;?php
</span><ins>+
+/* Path to the WordPress codebase you'd like to test. Add a backslash in the end. */
+define( 'ABSPATH', dirname( __FILE__ ) . '/wordpress/' );
+
</ins><span class="cx"> // ** MySQL settings ** //
</span><span class="cx"> 
</span><span class="cx"> // these will be used by the copy of wordpress being tested.
</span><span class="lines">@@ -8,12 +12,12 @@
</span><span class="cx"> // wp-test will DROP ALL TABLES in the database named below.
</span><span class="cx"> // DO NOT use a production database or one that is shared with something else.
</span><span class="cx"> 
</span><del>-define('DB_NAME', 'putyourdbnamehere');    // The name of the database
-define('DB_USER', 'usernamehere');     // Your MySQL username
-define('DB_PASSWORD', 'yourpasswordhere'); // ...and password
-define('DB_HOST', 'localhost');    // 99% chance you won't need to change this value
-define('DB_CHARSET', 'utf8');
-define('DB_COLLATE', '');
</del><ins>+define( 'DB_NAME', 'putyourdbnamehere' );    // The name of the database
+define( 'DB_USER', 'usernamehere' );     // Your MySQL username
+define( 'DB_PASSWORD', 'yourpasswordhere' ); // ...and password
+define( 'DB_HOST', 'localhost' );    // 99% chance you won't need to change this value
+define( 'DB_CHARSET', 'utf8' );
+define( 'DB_COLLATE', '' );
</ins><span class="cx"> 
</span><span class="cx"> // You can have multiple installations in one database if you give each a unique prefix
</span><span class="cx"> $table_prefix  = 'wp_';   // Only numbers, letters, and underscores please!
</span><span class="lines">@@ -22,8 +26,32 @@
</span><span class="cx"> // chosen language must be installed to wp-content/languages.
</span><span class="cx"> // For example, install de.mo to wp-content/languages and set WPLANG to 'de'
</span><span class="cx"> // to enable German language support.
</span><del>-define ('WPLANG', '');
</del><ins>+define ( 'WPLANG', '' );
</ins><span class="cx"> 
</span><del>-// uncomment and change this if you'd like to load plugins from a particular directory prior to testing
-#define('DIR_TESTPLUGINS', './wp-plugins');
-?&gt;
</del><ins>+define( 'WP_TESTS_DOMAIN', 'example.org' );
+define( 'WP_TESTS_EMAIL', 'admin@example.org' );
+define( 'WP_TESTS_TITLE', 'Test Blog' );
+define( 'WP_TESTS_NETWORK_TITLE', 'Test Network' );
+define( 'WP_TESTS_SUBDOMAIN_INSTALL', true );
+$base = '/';
+
+/* Cron tries to make an HTTP request to the blog, which always fails, because tests are run in CLI mode only */
+define( 'DISABLE_WP_CRON', true );
+
+define( 'WP_ALLOW_MULTISITE', false );
+if ( WP_ALLOW_MULTISITE ) {
+        define( 'WP_TESTS_BLOGS', 'first,second,third,fourth' );
+}
+if ( WP_ALLOW_MULTISITE &amp;&amp; ! defined('WP_INSTALLING') ) {
+        define( 'SUBDOMAIN_INSTALL', WP_TESTS_SUBDOMAIN_INSTALL );
+        define( 'MULTISITE', true );
+        define( 'DOMAIN_CURRENT_SITE', WP_TESTS_DOMAIN );
+        define( 'PATH_CURRENT_SITE', '/' );
+        define( 'SITE_ID_CURRENT_SITE', 1 );
+        define( 'BLOG_ID_CURRENT_SITE', 1 );
+        //define( 'SUNRISE', TRUE );
+}
+
+$table_prefix  = 'wp_';
+
+define( 'WP_PHP_BINARY', 'php' );
</ins></span></pre></div>
<a id="trunkwptestbininstallphp"></a>
<div class="addfile"><h4>Added: trunk/wp-testbin/install.php (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-testbin/install.php                                (rev 0)
+++ trunk/wp-testbin/install.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,61 @@
</span><ins>+&lt;?php
+/**
+ * Installs WordPress for the purpose of the unit-tests
+ *
+ * @todo Reuse the init/load code in init.php
+ */
+error_reporting( E_ALL &amp; ~E_DEPRECATED &amp; ~E_STRICT );
+
+$config_file_path = $argv[1];
+
+$config_dir = dirname( $config_file_path );
+
+define( 'WP_INSTALLING', true );
+require_once $config_file_path;
+require_once $config_dir . '/wp-testlib/functions.php';
+
+$_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';
+
+require_once ABSPATH . '/wp-admin/includes/upgrade.php';
+require_once ABSPATH . '/wp-includes/wp-db.php';
+
+define( 'WP_TESTS_VERSION_FILE', ABSPATH . '.wp-tests-version' );
+
+$wpdb-&gt;suppress_errors();
+$wpdb-&gt;hide_errors();
+$installed = $wpdb-&gt;get_var( &quot;SELECT option_value FROM $wpdb-&gt;options WHERE option_name = 'siteurl'&quot; );
+
+if ( $installed &amp;&amp; file_exists( WP_TESTS_VERSION_FILE ) ) {
+        $installed_version_hash = file_get_contents( WP_TESTS_VERSION_FILE );
+        if ( $installed_version_hash == test_version_check_hash() ) {
+                return;
+        }
+}
+$wpdb-&gt;query( 'SET storage_engine = INNODB;' );
+$wpdb-&gt;query( 'DROP DATABASE IF EXISTS '.DB_NAME.&quot;;&quot; );
+$wpdb-&gt;query( 'CREATE DATABASE '.DB_NAME.&quot;;&quot; );
+$wpdb-&gt;select( DB_NAME, $wpdb-&gt;dbh );
+
+echo &quot;Installing…&quot; . PHP_EOL;
+wp_install( WP_TESTS_TITLE, 'admin', WP_TESTS_EMAIL, true, '', 'a' );
+
+if ( defined('WP_ALLOW_MULTISITE') &amp;&amp; WP_ALLOW_MULTISITE ) {
+        echo &quot;Installing network…&quot; .PHP_EOL;
+
+        define( 'WP_INSTALLING_NETWORK', true );
+        //wp_set_wpdb_vars();
+        // We need to create references to ms global tables to enable Network.
+        foreach ( $wpdb-&gt;tables( 'ms_global' ) as $table =&gt; $prefixed_table )
+                $wpdb-&gt;$table = $prefixed_table;
+        install_network();
+        $result = populate_network(1, WP_TESTS_DOMAIN, WP_TESTS_EMAIL, WP_TESTS_NETWORK_TITLE, '/', WP_TESTS_SUBDOMAIN_INSTALL);
+
+        system( WP_PHP_BINARY . ' ' . escapeshellarg( dirname( __FILE__ ) . '/ms-install.php' ) . ' ' . escapeshellarg( $config_file_path ) );
+
+}
+
+file_put_contents( WP_TESTS_VERSION_FILE, test_version_check_hash() );
</ins></span></pre></div>
<a id="trunkwptestbinmsinstallphp"></a>
<div class="addfile"><h4>Added: trunk/wp-testbin/ms-install.php (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-testbin/ms-install.php                                (rev 0)
+++ trunk/wp-testbin/ms-install.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,39 @@
</span><ins>+&lt;?php
+/**
+ * Installs additional MU sites for the purpose of the unit-tests
+ *
+ * @todo Reuse the init/load code in init.php
+ */
+error_reporting( E_ALL &amp; ~E_DEPRECATED &amp; ~E_STRICT );
+
+$config_file_path = $argv[1];
+
+require_once $config_file_path;
+
+$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.1';
+$_SERVER['HTTP_HOST'] = WP_TESTS_DOMAIN;
+$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
+$PHP_SELF = $GLOBALS['PHP_SELF'] = $_SERVER['PHP_SELF'] = '/index.php';
+
+require_once ABSPATH . '/wp-settings.php';
+
+require_once ABSPATH . '/wp-admin/includes/upgrade.php';
+require_once ABSPATH . '/wp-includes/wp-db.php';
+
+echo &quot;Installing sites…&quot; . PHP_EOL;
+wp_install( WP_TESTS_TITLE, 'admin', WP_TESTS_EMAIL, true, '', 'a' );
+
+if ( defined('WP_ALLOW_MULTISITE') &amp;&amp; WP_ALLOW_MULTISITE ) {
+        $blogs = explode(',', WP_TESTS_BLOGS);
+        foreach ( $blogs as $blog ) {
+                if ( WP_TESTS_SUBDOMAIN_INSTALL ) {
+                        $newdomain = $blog.'.'.preg_replace( '|^www\.|', '', WP_TESTS_DOMAIN );
+                        $path = $base;
+                } else {
+                        $newdomain = WP_TESTS_DOMAIN;
+                        $path = $base.$blog.'/';
+                }
+                wpmu_create_blog( $newdomain, $path, $blog, email_exists(WP_TESTS_EMAIL) , array( 'public' =&gt; 1 ), 1 );
+
+        }
+}
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkwptestcasetest_simplephp"></a>
<div class="addfile"><h4>Added: trunk/wp-testcase/test_simple.php (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-testcase/test_simple.php                                (rev 0)
+++ trunk/wp-testcase/test_simple.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,10 @@
</span><ins>+&lt;?php
+
+/**
+ * Simple test to verify that PHPUnit is working correctly.
+ */
+class SimpleTest extends WP_UnitTestCase {
+        function test_simple() {
+                $this-&gt;assertTrue(true);
+        }
+}
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkwptestlibexceptionsphp"></a>
<div class="addfile"><h4>Added: trunk/wp-testlib/exceptions.php (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-testlib/exceptions.php                                (rev 0)
+++ trunk/wp-testlib/exceptions.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,5 @@
</span><ins>+&lt;?php
+
+class WP_Tests_Exception extends PHPUnit_Framework_Exception {
+        
+}
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkwptestlibfactoryphp"></a>
<div class="addfile"><h4>Added: trunk/wp-testlib/factory.php (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-testlib/factory.php                                (rev 0)
+++ trunk/wp-testlib/factory.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,189 @@
</span><ins>+&lt;?php
+
+class WP_UnitTest_Factory {
+        function __construct() {
+                $this-&gt;post = new WP_UnitTest_Factory_For_Post( $this );
+                $this-&gt;comment = new WP_UnitTest_Factory_For_Comment( $this );
+                $this-&gt;user = new WP_UnitTest_Factory_For_User( $this );
+        }
+}
+
+class WP_UnitTest_Factory_For_Post extends WP_UnitTest_Factory_For_Thing {
+
+        function __construct( $factory = null ) {
+                parent::__construct( $factory );
+                $this-&gt;default_generation_definitions = array(
+                        'post_status' =&gt; 'publish',
+                        'post_title' =&gt; new WP_UnitTest_Generator_Sequence( 'Post title %s' ),
+                        'post_content' =&gt; new WP_UnitTest_Generator_Sequence( 'Post content %s' ),
+                        'post_excerpt' =&gt; new WP_UnitTest_Generator_Sequence( 'Post excerpt %s' ),
+                        'post_type' =&gt; 'post'
+                );
+        }
+
+        function create_object( $args ) {
+                return wp_insert_post( $args );
+        }
+
+        function update_object( $post_id, $fields ) {
+                $fields['ID'] = $post_id;
+                return wp_update_post( $fields );
+        }
+}
+
+class WP_UnitTest_Factory_For_User extends WP_UnitTest_Factory_For_Thing {
+
+        function __construct( $factory = null ) {
+                parent::__construct( $factory );
+                $this-&gt;default_generation_definitions = array(
+                        'user_login' =&gt; new WP_UnitTest_Generator_Sequence( 'User %s' ),
+                        'user_pass' =&gt; 'a',
+                        'user_email' =&gt; new WP_UnitTest_Generator_Sequence( 'user_%s@example.org' ),
+                );
+        }
+
+        function create_object( $args ) {
+                return wp_insert_user( $args );
+        }
+
+        function update_object( $user_id, $fields ) {
+                $fields['ID'] = $user_id;
+                return wp_update_user( $fields );
+        }
+}
+
+class WP_UnitTest_Factory_For_Comment extends WP_UnitTest_Factory_For_Thing {
+
+        function __construct( $factory = null ) {
+                parent::__construct( $factory );
+                $this-&gt;default_generation_definitions = array(
+                        'comment_author' =&gt; new WP_UnitTest_Generator_Sequence( 'Commenter %s' ),
+                        'comment_author_url' =&gt; new WP_UnitTest_Generator_Sequence( 'http://example.com/%s/' ),
+                        'comment_approved' =&gt; 1,
+                );
+        }
+
+        function create_object( $args ) {
+                return wp_insert_comment( $args );
+        }
+
+        function update_object( $comment_id, $fields ) {
+                $fields['comment_ID'] = $comment_id;
+                return wp_update_comment( $fields );
+        }
+
+        function create_post_comments( $post_id, $count = 1, $args = array(), $generation_definitions = null ) {
+                $args['comment_post_ID'] = $post_id;
+                return $this-&gt;create_many( $count, $args, $generation_definitions );
+        }
+}
+
+
+abstract class WP_UnitTest_Factory_For_Thing {
+
+        var $default_generation_definitions;
+        var $factory;
+
+        /**
+         * Creates a new factory, which will create objects of a specific Thing
+         *
+         * @param object $factory Global factory that can be used to create other objects on the system
+         * @param array $default_generation_definitions Defines what default values should the properties of the object have. The default values
+         * can be generators -- an object with next() method. There are some default generators: {@link WP_UnitTest_Generator_Sequence},
+         * {@link WP_UnitTest_Generator_Locale_Name}, {@link WP_UnitTest_Factory_Callback_After_Create}.
+         */
+        function __construct( $factory, $default_generation_definitions = array() ) {
+                $this-&gt;factory = $factory;
+                $this-&gt;default_generation_definitions = $default_generation_definitions;
+        }
+
+        abstract function create_object( $args );
+        abstract function update_object( $object, $fields );
+
+        function create( $args = array(), $generation_definitions = null ) {
+                if ( is_null( $generation_definitions ) )
+                        $generation_definitions = $this-&gt;default_generation_definitions;
+
+                $generated_args = $this-&gt;generate_args( $args, $generation_definitions, $callbacks );
+                $created = $this-&gt;create_object( $generated_args );
+                if ( !$created || is_wp_error( $created ) )
+                        return $created;
+
+                if ( $callbacks ) {
+                        $updated_fields = $this-&gt;apply_callbacks( $callbacks, $created );
+                        $save_result = $this-&gt;update_object( $created, $updated_fields );
+                        if ( !$save_result || is_wp_error( $save_result ) )
+                                return $save_result;
+                }
+                return $created;
+        }
+
+        function create_many( $count, $args = array(), $generation_definitions = null ) {
+                $results = array();
+                for ( $i = 0; $i &lt; $count; $i++ ) {
+                        $results[] = $this-&gt;create( $args, $generation_definitions );
+                }
+                return $results;
+        }
+
+        function generate_args( $args = array(), $generation_definitions = null, &amp;$callbacks = null ) {
+                $callbacks = array();
+                if ( is_null( $generation_definitions ) )
+                        $generation_definitions = $this-&gt;default_generation_definitions;
+
+                foreach( array_keys( $generation_definitions ) as $field_name ) {
+                        if ( !isset( $args[$field_name] ) ) {
+                                $generator = $generation_definitions[$field_name];
+                                if ( is_scalar( $generator ) )
+                                        $args[$field_name] = $generator;
+                                elseif ( is_object( $generator ) &amp;&amp; method_exists( $generator, 'call' ) ) {
+                                        $callbacks[$field_name] = $generator;
+                                } elseif ( is_object( $generator ) )
+                                        $args[$field_name] = $generator-&gt;next();
+                                else
+                                        return new WP_Error( 'invalid_argument', 'Factory default value should be either a scalar or an generator object.' );
+                        }
+                }
+                return $args;
+        }
+
+        function apply_callbacks( $callbacks, $created ) {
+                $updated_fields = array();
+                foreach( $callbacks as $field_name =&gt; $generator ) {
+                        $updated_fields[$field_name] = $generator-&gt;call( $created );
+                }
+                return $updated_fields;
+        }
+
+        function callback( $function ) {
+                return new WP_UnitTest_Factory_Callback_After_Create( $function );
+        }
+}
+
+class WP_UnitTest_Generator_Sequence {
+        var $next;
+        var $template_string;
+
+        function __construct( $template_string = '%s', $start = 1 ) {
+                $this-&gt;next = $start;
+                $this-&gt;template_string = $template_string;
+        }
+
+        function next() {
+                $generated = sprintf( $this-&gt;template_string , $this-&gt;next );
+                $this-&gt;next++;
+                return $generated;
+        }
+}
+
+class WP_UnitTest_Factory_Callback_After_Create {
+        var $callback;
+
+        function __construct( $callback ) {
+                $this-&gt;callback = $callback;
+        }
+
+        function call( $object ) {
+                return call_user_func( $this-&gt;callback, $object );
+        }
+}
</ins></span></pre></div>
<a id="trunkwptestlibfunctionsphp"></a>
<div class="addfile"><h4>Added: trunk/wp-testlib/functions.php (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-testlib/functions.php                                (rev 0)
+++ trunk/wp-testlib/functions.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,60 @@
</span><ins>+&lt;?php
+/**
+ * Generate a hash to be used when comparing installed version against
+ * codebase and current configuration
+ * @return string $hash sha1 hash
+ **/
+function test_version_check_hash() {
+        $hash = '';
+        $db_version = get_option( 'db_version' );
+        if ( defined('WP_ALLOW_MULTISITE') &amp;&amp; WP_ALLOW_MULTISITE ) {
+                $version = $db_version;
+                if( defined( 'WP_TESTS_BLOGS' ) ) {
+                        $version .= WP_TESTS_BLOGS;
+                }
+                if( defined( 'WP_TESTS_SUBDOMAIN_INSTALL' ) ) {
+                        $version .= WP_TESTS_SUBDOMAIN_INSTALL;
+                }
+                if( defined( 'WP_TESTS_DOMAIN' ) ) {
+                        $version .= WP_TESTS_DOMAIN;
+                }
+
+        } else {
+                $version = $db_version;
+        }
+
+        return sha1($version);
+}
+
+// For adding hooks before loading WP
+function tests_add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
+        global $wp_filter, $merged_filters;
+
+        $idx = _test_filter_build_unique_id($tag, $function_to_add, $priority);
+        $wp_filter[$tag][$priority][$idx] = array('function' =&gt; $function_to_add, 'accepted_args' =&gt; $accepted_args);
+        unset( $merged_filters[ $tag ] );
+        return true;
+}
+
+function _test_filter_build_unique_id($tag, $function, $priority) {
+        global $wp_filter;
+        static $filter_id_count = 0;
+
+        if ( is_string($function) )
+                return $function;
+
+        if ( is_object($function) ) {
+                // Closures are currently implemented as objects
+                $function = array( $function, '' );
+        } else {
+                $function = (array) $function;
+        }
+
+        if (is_object($function[0]) ) {
+                return spl_object_hash($function[0]) . $function[1];
+        } else if ( is_string($function[0]) ) {
+                // Static Calling
+                return $function[0].$function[1];
+        }
+}
+
</ins></span></pre></div>
<a id="trunkwptestlibtestcasephp"></a>
<div class="addfile"><h4>Added: trunk/wp-testlib/testcase.php (0 => 760)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-testlib/testcase.php                                (rev 0)
+++ trunk/wp-testlib/testcase.php        2012-06-30 17:30:59 UTC (rev 760)
</span><span class="lines">@@ -0,0 +1,112 @@
</span><ins>+&lt;?php
+
+require_once 'factory.php';
+
+class WP_UnitTestCase extends PHPUnit_Framework_TestCase {
+
+        function setUp() {
+                global $wpdb;
+                $wpdb-&gt;suppress_errors = false;
+                $wpdb-&gt;show_errors = true;
+                $wpdb-&gt;db_connect();
+                ini_set('display_errors', 1 );
+                $this-&gt;factory = new WP_UnitTest_Factory;
+                $this-&gt;clean_up_global_scope();
+                $this-&gt;start_transaction();
+        }
+
+        function tearDown() {
+                global $wpdb;
+                $wpdb-&gt;query( 'ROLLBACK' );
+        }
+
+        function clean_up_global_scope() {
+                $_GET = array();
+                $_POST = array();
+                $this-&gt;flush_cache();
+        }
+
+        function flush_cache() {
+                global $wp_object_cache;
+                $wp_object_cache-&gt;group_ops = array();
+                $wp_object_cache-&gt;stats = array();
+                $wp_object_cache-&gt;memcache_debug = array();
+                $wp_object_cache-&gt;cache = array();
+                if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
+                        $wp_object_cache-&gt;__remoteset();
+                }
+                wp_cache_flush();
+        }
+
+        function start_transaction() {
+                global $wpdb;
+                $wpdb-&gt;query( 'SET autocommit = 0;' );
+                $wpdb-&gt;query( 'START TRANSACTION;' );
+        }
+
+        function assertWPError( $actual, $message = '' ) {
+                $this-&gt;assertTrue( is_wp_error( $actual ), $message );
+        }
+
+        function assertEqualFields( $object, $fields ) {
+                foreach( $fields as $field_name =&gt; $field_value ) {
+                        if ( $object-&gt;$field_name != $field_value ) {
+                                $this-&gt;fail();
+                        }
+                }
+        }
+
+        function assertDiscardWhitespace( $expected, $actual ) {
+                $this-&gt;assertEquals( preg_replace( '/\s*/', '', $expected ), preg_replace( '/\s*/', '', $actual ) );
+        }
+
+        function checkAtLeastPHPVersion( $version ) {
+                if ( version_compare( PHP_VERSION, $version, '&lt;' ) ) {
+                        $this-&gt;markTestSkipped();
+                }
+        }
+
+        function go_to( $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 {
+                                $parts['query'] = '';
+                        }
+                }
+                else {
+                        $req = $url;
+                }
+
+                $_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']);
+        }
+
+}
</ins></span></pre>
</div>
</div>

</body>
</html>