262 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			262 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
/*
 | 
						|
 * Name: SimpleTest integration
 | 
						|
 * Author: Shish <webmaster@shishnet.org>
 | 
						|
 * Link: http://code.shishnet.org/shimmie2/
 | 
						|
 * License: GPLv2
 | 
						|
 * Description: adds unit testing to SCore
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * \page unittests Unit Tests
 | 
						|
 * 
 | 
						|
 * Each extension should (although doesn't technically have to) come with a
 | 
						|
 * test.php file, for example ext/index/test.php. The SimpleSCoreTest
 | 
						|
 * extension will look for these files and load any SCoreWebTestCase classes
 | 
						|
 * it finds inside them, then run them and report whether or not the test
 | 
						|
 * passes.
 | 
						|
 * 
 | 
						|
 * For Shimmie2 specific extensions, there is a ShimmieWebTestCase class which
 | 
						|
 * includes functions to upload and delete images.
 | 
						|
 *
 | 
						|
 * For a quick guide on the spcifics of how to write tests, see \ref wut
 | 
						|
 *
 | 
						|
 *
 | 
						|
 * \page wut Writing Unit Tests
 | 
						|
 *
 | 
						|
 * Note: The unit test framework assumes a fresh, default install with an
 | 
						|
 * admin called "demo" and a user called "test". The full test suite takes
 | 
						|
 * a few minutes to run on my test VM, which is long enough that my shared
 | 
						|
 * hosting provider cuts it off half way through. Running tests for specific
 | 
						|
 * extensions (visit "/test/extension_folder_name") is generally OK though.
 | 
						|
 *
 | 
						|
 * An empty test class starts like so (assuming the extension we're writing
 | 
						|
 * tests for is called "Example")
 | 
						|
 *
 | 
						|
 * \code
 | 
						|
 * <?php
 | 
						|
 * class ExampleTest extends SCoreWebTestCase {
 | 
						|
 *     public function test_blah() {
 | 
						|
 *     }
 | 
						|
 * }
 | 
						|
 * ?>
 | 
						|
 * \endcode
 | 
						|
 *
 | 
						|
 * SCoreWebTestCase is for testing generic extensions, ShimmieWebTestCase is
 | 
						|
 * for imageboard-specific extensions. The name of the function doesn't matter
 | 
						|
 * as long as it begins with "test". If you want to test several parts of the
 | 
						|
 * extension independantly, you can write several functions, just make sure
 | 
						|
 * that each begins with "test".
 | 
						|
 *
 | 
						|
 * Once you're in the function, $this is a reference to a virtual web browser
 | 
						|
 * which you can control with code. The functions available are visible in the
 | 
						|
 * docs for class SCoreWebTestCase and ShimmieWebTestCase.
 | 
						|
 *
 | 
						|
 * Basically, just simulate a browsing session, making sure that everything
 | 
						|
 * is where it's supposed to be. If you can simulate a browsing session that
 | 
						|
 * triggers a bug, then it makes that bug much easier for developers to fix,
 | 
						|
 * and will make sure that it doesn't come back.
 | 
						|
 *
 | 
						|
 * \code
 | 
						|
 * <?php
 | 
						|
 * class ExampleTest extends SCoreWebTestCase {
 | 
						|
 *     public function test_blah() {
 | 
						|
 *         $this->get_page("my/page");
 | 
						|
 *         $this->assert_title("My Page Title");
 | 
						|
 *         $this->assert_text("This is my page");
 | 
						|
 *         $this->click("a link to my other page");
 | 
						|
 *         $this->assert_title("My Other Page");
 | 
						|
 *         $this->back();
 | 
						|
 *         $this->assert_title("My Page Title");
 | 
						|
 *         $this->set_field("some_text", "Here is some text");
 | 
						|
 *         $this->click("Send Some Text");
 | 
						|
 *         $this->assert_text("Your input was: Here is some text");
 | 
						|
 *     }
 | 
						|
 * }
 | 
						|
 * ?>
 | 
						|
 * \endcode
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
require_once('lib/simpletest/web_tester.php');
 | 
						|
require_once('lib/simpletest/unit_tester.php');
 | 
						|
require_once('lib/simpletest/reporter.php');
 | 
						|
 | 
						|
define('USER_NAME', "test");
 | 
						|
define('USER_PASS', "test");
 | 
						|
define('ADMIN_NAME', "demo");
 | 
						|
define('ADMIN_PASS', "demo");
 | 
						|
 | 
						|
/**
 | 
						|
 * A set of common SCore activities to test
 | 
						|
 */
 | 
						|
class SCoreWebTestCase extends WebTestCase {
 | 
						|
 | 
						|
 	/**
 | 
						|
	 * Click on a link or a button
 | 
						|
	 */
 | 
						|
 	public function click($text) {
 | 
						|
		return parent::click($text);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Click the virtual browser's back button
 | 
						|
	 */
 | 
						|
	public function back() {
 | 
						|
		return parent::back();
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Get a page based on the SCore URL, eg get_page("post/list") will do
 | 
						|
	 * the right thing; no need for http:// or any such
 | 
						|
	 */
 | 
						|
	protected function get_page($page) {
 | 
						|
		// Check if we are running on the command line
 | 
						|
		if(php_sapi_name() == 'cli' || $_SERVER['HTTP_HOST'] == "<cli command>") {
 | 
						|
			$host = constant("_TRAVIS_WEBHOST");
 | 
						|
			$this->assertFalse(empty($host)); // Make sure that we know the host address.
 | 
						|
			$raw = $this->get($host."/index.php?q=".str_replace("?", "&", $page));
 | 
						|
		}
 | 
						|
		else {
 | 
						|
			$raw = $this->get(make_http(make_link($page)));
 | 
						|
		}
 | 
						|
		$this->assertNoText("Internal Error");
 | 
						|
		$this->assertNoText("Exception:");
 | 
						|
		$this->assertNoText("Error:");
 | 
						|
		$this->assertNoText("Warning:");
 | 
						|
		$this->assertNoText("Notice:");
 | 
						|
		return $raw;
 | 
						|
	}
 | 
						|
 | 
						|
	protected function log_in_as_user() {
 | 
						|
        $this->get_page('user_admin/login');
 | 
						|
		$this->assertText("Login");
 | 
						|
		$this->setField('user', USER_NAME);
 | 
						|
		$this->setField('pass', USER_PASS);
 | 
						|
		$this->click("Log In");
 | 
						|
	}
 | 
						|
 | 
						|
	protected function log_in_as_admin() {
 | 
						|
        $this->get_page('user_admin/login');
 | 
						|
		$this->assertText("Login");
 | 
						|
		$this->setField('user', ADMIN_NAME);
 | 
						|
		$this->setField('pass', ADMIN_PASS);
 | 
						|
		$this->click("Log In");
 | 
						|
	}
 | 
						|
 | 
						|
	protected function log_out() {
 | 
						|
        $this->get_page('post/list');
 | 
						|
		$this->click('Log Out');
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * Look through the HTML for a form element with the name $name,
 | 
						|
	 * set its value to $value
 | 
						|
	 */
 | 
						|
	protected function set_field($name, $value) {
 | 
						|
		parent::setField($name, $value);
 | 
						|
	}
 | 
						|
 | 
						|
	protected function assert_text($text) {parent::assertText($text);}
 | 
						|
	protected function assert_title($title) {parent::assertTitle($title);}
 | 
						|
	protected function assert_no_text($text) {parent::assertNoText($text);}
 | 
						|
	protected function assert_mime($type) {parent::assertMime($type);}
 | 
						|
	protected function assert_response($code) {parent::assertResponse($code);}
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * A set of common Shimmie activities to test
 | 
						|
 */
 | 
						|
class ShimmieWebTestCase extends SCoreWebTestCase {
 | 
						|
	protected function post_image($filename, $tags) {
 | 
						|
		$image_id = -1;
 | 
						|
		$this->setMaximumRedirects(0);
 | 
						|
 | 
						|
        $this->get_page('post/list');
 | 
						|
		$this->assertText("Upload");
 | 
						|
		$this->setField("data0", $filename);
 | 
						|
		$this->setField("tags", $tags);
 | 
						|
		$this->click("Post");
 | 
						|
 | 
						|
		$raw_headers = $this->getBrowser()->getHeaders();
 | 
						|
		$headers = explode("\n", $raw_headers);
 | 
						|
		foreach($headers as $header) {
 | 
						|
			$parts = explode(":", $header);
 | 
						|
			if(trim($parts[0]) == "X-Shimmie-Image-ID") {
 | 
						|
				$image_id = int_escape(trim($parts[1]));
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// sometimes we want uploading to fail, eg
 | 
						|
		// testing for a blacklist
 | 
						|
		//$this->assertTrue($image_id > 0);
 | 
						|
 | 
						|
		$this->setMaximumRedirects(5);
 | 
						|
		return $image_id;
 | 
						|
	}
 | 
						|
 | 
						|
	protected function delete_image($image_id) {
 | 
						|
		if($image_id > 0) {
 | 
						|
	        $this->get_page('post/view/'.$image_id);
 | 
						|
			$this->clickSubmit("Delete");
 | 
						|
			// Make sure that the image is really deleted.
 | 
						|
			//$this->get_page('post/view/'.$image_id);
 | 
						|
			//$this->assert_response(404);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/** @private */
 | 
						|
class TestFinder extends TestSuite {
 | 
						|
	function __construct($hint) {
 | 
						|
		if(strpos($hint, "..") !== FALSE) return;
 | 
						|
		
 | 
						|
		// Select the test cases for "All" extensions.
 | 
						|
		$dir = "{".ENABLED_EXTS."}";
 | 
						|
		
 | 
						|
		// Unless the user specified just a specific extension.
 | 
						|
		if(file_exists("ext/$hint/test.php")) $dir = $hint;
 | 
						|
		
 | 
						|
		$this->TestSuite('All tests');
 | 
						|
		
 | 
						|
		foreach(zglob("ext/$dir/test.php") as $file) {
 | 
						|
			$this->addFile($file);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
class SimpleSCoreTest extends Extension {
 | 
						|
	public function onPageRequest(PageRequestEvent $event) {
 | 
						|
		global $page;
 | 
						|
		if($event->page_matches("test")) {
 | 
						|
			set_time_limit(0);
 | 
						|
 | 
						|
			$page->set_title("Test Results");
 | 
						|
			$page->set_heading("Test Results");
 | 
						|
			$page->add_block(new NavBlock());
 | 
						|
 | 
						|
			$all = new TestFinder($event->get_arg(0));
 | 
						|
			$all->run(new SCoreWebReporter($page));
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	public function onCommand(CommandEvent $event) {
 | 
						|
		if($event->cmd == "help") {
 | 
						|
			print "  test [extension]\n";
 | 
						|
			print "    run automated tests for the name extension\n\n";
 | 
						|
		}
 | 
						|
		if($event->cmd == "test") {
 | 
						|
			$all = new TestFinder($event->args[0]);
 | 
						|
			$all->run(new SCoreCLIReporter());
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	public function onUserBlockBuilding(UserBlockBuildingEvent $event) {
 | 
						|
		global $user;
 | 
						|
		if($user->is_admin()) {
 | 
						|
			$event->add_link("Run Tests", make_link("test/all"));
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 |