kolibri-discuss team mailing list archive
-
kolibri-discuss team
-
Mailing list archive
-
Message #00044
[Merge] lp:~stian-prestholdt/kolibri/test-framework into lp:kolibri
Anders Steinlein has proposed merging lp:~stian-prestholdt/kolibri/test-framework into lp:kolibri.
Requested reviews:
Kolibri Dev (kolibri-dev)
--
https://code.launchpad.net/~stian-prestholdt/kolibri/test-framework/+merge/6010
Your team Kolibri Discuss is subscribed to branch lp:kolibri.
=== removed file 'examples/wishlist/conf/development.ini'
--- examples/wishlist/conf/development.ini 2009-04-14 21:48:11 +0000
+++ examples/wishlist/conf/development.ini 1970-01-01 00:00:00 +0000
@@ -1,1 +0,0 @@
-; Empty for this simple example application.
\ No newline at end of file
=== added file 'examples/wishlist/kolibri.php'
--- examples/wishlist/kolibri.php 1970-01-01 00:00:00 +0000
+++ examples/wishlist/kolibri.php 2009-04-27 13:27:48 +0000
@@ -0,0 +1,41 @@
+<?php
+/**
+ * The HTTP gateway to the Kolibri framework. Essential core files are included, config
+ * initialized and processing of the HTTP request is started.
+ * The constants defined here, ROOT and APP_PATH, can be changed from their default values by
+ * using SetEnv in the .htaccess file or in an appropriate Apache config directive.
+ */
+
+/*
+ * Defines the root directory of the Kolibri framework. By default this is a directory named
+ * 'kolibri' within the document root.
+ */
+if ($rootDir = getenv('KOLIBRI_ROOT')) {
+ define('ROOT', $rootDir);
+}
+else {
+ define('ROOT', dirname(__FILE__) . '/kolibri');
+}
+
+/*
+ * Defines the root directory for the application. By default this is the same directory as
+ * this kolibri.php file.
+ */
+if ($appDir = getenv('KOLIBRI_APP')) {
+ define('APP_PATH', $appDir);
+}
+else {
+ define('APP_PATH', dirname(__FILE__));
+}
+
+// Require essential files. Others are loaded as needed.
+require(ROOT . '/core/Config.php');
+require(ROOT . '/core/RequestProcessor.php');
+
+// Init configuration
+Config::getInstance();
+
+$request = new Request($_GET, $_POST);
+$kolibri = new RequestProcessor($request);
+$kolibri->process();
+?>
=== modified file 'examples/wishlist/models/dao/ItemDao.php'
--- examples/wishlist/models/dao/ItemDao.php 2008-12-04 01:32:42 +0000
+++ examples/wishlist/models/dao/ItemDao.php 2009-04-20 12:53:54 +0000
@@ -18,7 +18,7 @@
SQL;
return $db->getObject('Item', $query, $id); // Gets one object of type Item
}
-
+
/**
* findAll() is here used to retrieve, well, all [relevant] objects.
*/
@@ -50,7 +50,11 @@
public static function update ($item) {
$db = DatabaseFactory::getConnection();
$query = <<<SQL
- UPDATE items SET received = :received WHERE name = :name
+ UPDATE items SET received = :received,
+ name = :name,
+ description = :description,
+ price = :price
+ WHERE name = :name
SQL;
return $db->query($query, $item);
}
=== added directory 'examples/wishlist/specs'
=== added file 'examples/wishlist/specs/SpecHelper.php'
--- examples/wishlist/specs/SpecHelper.php 1970-01-01 00:00:00 +0000
+++ examples/wishlist/specs/SpecHelper.php 2009-04-28 06:14:00 +0000
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Loads all the correct files and mode for KolibriTestCase
+ *
+ * REMEMBER to require this file in every spec class you have
+ * <code>require_once(dirname(__FILE__) . '/../SpecHelper.php')</code>
+ *
+ * Defines the root directory of the Kolibri framework. By default this is a directory named
+ * 'kolibri' within the document root.
+ */
+if (!defined('ROOT')) {
+ if (!$rootDir = getenv('KOLIBRI_ROOT')) {
+ throw new Exception('Environment variable KOLIBRI_ROOT must be defined.');
+ exit;
+ }
+ define('ROOT', $rootDir);
+
+ /*
+ * Defines the root directory for the application. By default this is the same directory as
+ * this kolibri.php file.
+ */
+ $dirname = dirname(__FILE__);
+ if (basename($dirname) == 'specs') {
+ $path = dirname(__FILE__) . '/..';
+ }
+ else $path = dirname(__FILE__) . '/../..';
+
+ define('APP_PATH', $path);
+}
+
+putenv('KOLIBRI_MODE=test');
+require(ROOT . '/core/Config.php');
+require(ROOT . '/core/RequestProcessor.php');
+
+Config::getInstance();
+
+require(ROOT . '/specs/Fixtures.php');
+require(ROOT . '/specs/KolibriContext.php');
+
+$setupFile = APP_PATH . '/specs/setup.sql';
+$schemaFile = APP_PATH . '/config/schema.sql';
+
+if (file_exists($setupFile)) {
+ $db = DatabaseFactory::getConnection();
+
+ if(file_exists($schemaFile)) {
+ $schemaContents = file_get_contents($schemaFile);
+ $db->batchQuery($schemaContents);
+ $db->commit();
+ }
+
+ $setupContents = file_get_contents($setupFile);
+ $db->batchQuery($setupContents);
+ $db->commit();
+}
+?>
=== added directory 'examples/wishlist/specs/actions'
=== added file 'examples/wishlist/specs/actions/DescribeIndexAction.php'
--- examples/wishlist/specs/actions/DescribeIndexAction.php 1970-01-01 00:00:00 +0000
+++ examples/wishlist/specs/actions/DescribeIndexAction.php 2009-04-28 06:28:42 +0000
@@ -0,0 +1,16 @@
+<?php
+require_once(dirname(__FILE__) . '/../SpecHelper.php');
+
+class DescribeIndexAction extends KolibriContext {
+ public function itShouldReturnXsltResponse () {
+ $this->get('/');
+ $this->spec($this->response)->should->beAnInstanceOf('XsltResponse');
+ }
+
+ public function itShouldHaveModelSetWhenInSession () {
+ $item = Models::getModel(new Item());
+ $this->get('/', array(), array('model' => $item));
+ $this->spec($this->action->model)->should->beAnInstanceOf('ModelProxy');
+ }
+}
+?>
\ No newline at end of file
=== added directory 'examples/wishlist/specs/actions/items'
=== added file 'examples/wishlist/specs/actions/items/DescribeItemAddAction.php'
--- examples/wishlist/specs/actions/items/DescribeItemAddAction.php 1970-01-01 00:00:00 +0000
+++ examples/wishlist/specs/actions/items/DescribeItemAddAction.php 2009-04-28 06:28:42 +0000
@@ -0,0 +1,21 @@
+<?php
+require_once(dirname(__FILE__) . '/../../SpecHelper.php');
+
+class DescribeItemAddAction extends KolibriContext {
+ public function itShouldSetModelInSessionWhenParamsAreInvalid () {
+ $this->post('/items/add');
+ $this->spec($this->request->session->get('model'))->should->beAnInstanceOf('ModelProxy');
+ }
+
+ public function itShouldHaveInvalidModelWhenParamsAreInvalid () {
+ $this->post('/items/add');
+ $this->spec($this->action->model)->shouldNot->beValid();
+ }
+
+ public function itShouldHaveValidModelWhenParamsAreValid () {
+ $this->post('/items/add', array('name' => 'A thing'));
+ $this->spec($this->action->model)->should->validate();
+ $this->spec($this->action->msg->getMessage())->should->be('Item successfully added.');
+ }
+}
+?>
=== added directory 'examples/wishlist/specs/fixtures'
=== added file 'examples/wishlist/specs/fixtures/Item.ini'
--- examples/wishlist/specs/fixtures/Item.ini 1970-01-01 00:00:00 +0000
+++ examples/wishlist/specs/fixtures/Item.ini 2009-04-20 11:13:57 +0000
@@ -0,0 +1,17 @@
+; fixture for item model
+
+[ValidItem]
+name = Playstation 3
+description = Game console
+price = 3400
+
+[AnotherItem]
+name = Xbox 360
+description = Game console numer 33
+price = 2100
+
+
+[InvalidItem]
+name =
+description =
+price =
=== added directory 'examples/wishlist/specs/models'
=== added file 'examples/wishlist/specs/models/DescribeItemModel.php'
--- examples/wishlist/specs/models/DescribeItemModel.php 1970-01-01 00:00:00 +0000
+++ examples/wishlist/specs/models/DescribeItemModel.php 2009-04-29 07:09:39 +0000
@@ -0,0 +1,99 @@
+<?php
+require_once(dirname(__FILE__) . '/../SpecHelper.php');
+
+/**
+ * Specification for the Item Model.
+ *
+ * To execute something before or after etc, you have helper methods like:
+ * <code>setup()</code>, <code>posteSpec()</code>, <code>preSpec()</code>
+ * and <code>tearDown()</code> that you can use in your testcase.
+ */
+class DescribeItemModel extends KolibriContext {
+ public $itemName;
+
+ /**
+ * This method acts as before() method from PHPSpec
+ */
+ public function setup () {
+ // This method doesnt have to be here if its blank.
+ $this->itemName = "Toy house";
+ }
+
+ /**
+ * Checks validation for an valid item model.
+ */
+ public function itShouldValidateWithValidData () {
+ $item = $this->fixtures['ValidItem'];
+ $this->spec($item)->should->beValid();
+ }
+
+ /**
+ * Checks validation for an invalid item model.
+ */
+ public function itShouldInvalidateWithInvalidData () {
+ $item = $this->fixtures['InvalidItem'];
+ $this->spec($item)->shouldNot->beValid();
+ }
+
+ /**
+ * This spec will try to save an item object
+ */
+ public function itShouldBeAbleToSave () {
+ $item = $this->fixtures['ValidItem'];
+ $this->spec($item->save())->should->beEqualTo(1);
+ }
+
+ /**
+ * This spec will try to load a saved item object
+ */
+ public function itShouldBeAbleToLoad () {
+ $item = Models::init('Item');
+ $item->objects->load($this->itemName);
+ $this->spec($item->name)->should->beEqualTo($this->itemName);
+ }
+
+ /**
+ * This spec will try to save an invalid item object
+ */
+ public function itShouldNotBeAbleToSaveAnInvalidItem () {
+ $item = $this->fixtures['InvalidItem'];
+ $this->spec($item->save())->should->beEqualTo(0);
+ }
+
+ /**
+ * This spec will try to update an item object
+ */
+ public function itShouldBeAbleToUpdate () {
+ $item = Models::init('Item');
+ $item->objects->load($this->itemName);
+ $item->description = "Test update";
+ $this->spec($item->save())->should->beEqualTo(1);
+
+ $item = Models::init('Item');
+ $item->objects->load($this->itemName);
+ $this->spec($item->description)->should->beEqualTo("Test update");
+ }
+
+ /**
+ * This spec will try to delete a saved item object
+ */
+ public function itShouldBeAbleToDelete () {
+ $item = Models::init('Item');
+ $item->objects->load($this->itemName);
+ $item->delete();
+
+ $item = Models::init('Item');
+ $item->objects->load($this->itemName);
+ $this->spec($item->name)->should->beEmpty();
+ }
+
+ /**
+ * This method acts as after() method from PHPSpec
+ */
+ public function tearDown () {
+ unset($this->itemName);
+ }
+
+
+}
+?>
=== added file 'examples/wishlist/specs/setup.sql'
--- examples/wishlist/specs/setup.sql 1970-01-01 00:00:00 +0000
+++ examples/wishlist/specs/setup.sql 2009-04-27 13:27:48 +0000
@@ -0,0 +1,15 @@
+DROP TABLE items;
+
+CREATE TABLE items (
+ name TEXT PRIMARY KEY NOT NULL,
+ description TEXT DEFAULT NULL,
+ price NUMERIC DEFAULT NULL,
+ added DATE NOT NULL,
+ received DATE DEFAULT NULL
+);
+
+INSERT INTO items (name, description, price, added, received)
+VALUES('Blue Bike', 'My favorite bike in the shop', 299, '2009-04-20', '2009-04-21');
+
+INSERT INTO items (name, description, price, added, received)
+VALUES('Toy house', 'Garden toy house with several floors', 1199, '2009-04-14', '2009-04-20');
\ No newline at end of file
=== added file 'examples/wishlist/test.php'
--- examples/wishlist/test.php 1970-01-01 00:00:00 +0000
+++ examples/wishlist/test.php 2009-04-27 13:27:48 +0000
@@ -0,0 +1,24 @@
+<?php
+/**
+ * PHPSpec
+ * This file will run all specs within this directory and all child-directories.
+ * TODO safeguard sjekke at mode = test
+ */
+
+
+chdir('specs');
+
+require_once 'PHPSpec.php';
+
+$options = new stdClass;
+$options->recursive = true;
+$options->specdoc = true;
+$options->reporter = 'html';
+
+ob_start();
+
+PHPSpec_Runner::run($options);
+
+ob_end_flush();
+
+?>
\ No newline at end of file
=== modified file 'src/.htaccess'
--- src/.htaccess 2008-10-20 16:41:10 +0000
+++ src/.htaccess 2009-04-16 12:05:45 +0000
@@ -5,4 +5,5 @@
# static resources (as is recommended), this should be modified.
RewriteRule ^favicon.ico$ static/favicon.ico [L]
RewriteCond %{REQUEST_URI} !^/static
+RewriteCond %{REQUEST_URI} !test.php$
RewriteRule ^(.*)$ kolibri.php
=== modified file 'src/core/Session.php'
--- src/core/Session.php 2009-04-20 16:03:03 +0000
+++ src/core/Session.php 2009-04-27 13:27:48 +0000
@@ -31,7 +31,8 @@
* Actually starts the session.
*/
private function start () {
- if (PHP_SAPI != 'cli') {
+
+ if (PHP_SAPI != 'cli' && session_id() === '') {
session_start();
}
$this->started = true;
=== modified file 'src/database/PostgreSqlConnection.php' (properties changed: -x to +x)
--- src/database/PostgreSqlConnection.php 2009-04-15 22:00:10 +0000
+++ src/database/PostgreSqlConnection.php 2009-04-29 07:09:39 +0000
@@ -160,6 +160,21 @@
throw new DatabaseException('Query could not be sent to the database. Connection lost?');
}
+
+ /**
+ * Sends several queries to the database after escaping and interpolating the supplied parameters.
+ *
+ * TODO: we want that every batchQuery will return the same, so this method has to return the number
+ * of changes in the database.
+ *
+ * @param string $query The query to execute.
+ * @param mixed $params Parameters to interpolate into query.
+ * @throws Exception Upon an error when executing the query.
+ * @return ResultSet Representing the query results. Implementation-specific.
+ */
+ public function batchQuery ($query, $params = null) {
+ return $this->query($query, $params);
+ }
/**
* Escapes a value to make it safe for use in SQL queries.
=== modified file 'src/database/SqliteConnection.php' (properties changed: -x to +x)
--- src/database/SqliteConnection.php 2009-04-21 17:15:47 +0000
+++ src/database/SqliteConnection.php 2009-04-28 06:11:20 +0000
@@ -149,7 +149,51 @@
// XXX: Should perhaps pass some kind of SQL error state as code
throw new SqlException($error, $preparedQuery);
}
-
+
+
+ /**
+ * Sends several queries to the database after escaping and interpolating the supplied parameters, and
+ * returns number of changes in the database.
+ *
+ * If a connection to the database is not yet established, <code>connect()</code> is called
+ * implicitly. The same is true of transactions; if a transaction has not yet been started on the
+ * connection, <code>begin()</code> is called.
+ *
+ * @param string $query The query to execute.
+ * @param mixed $params Parameters to interpolate into query.
+ * @throws Exception Upon an error when executing the query.
+ * @return ResultSet Representing the query results. Implementation-specific.
+ */
+ public function batchQuery ($query, $params = null) {
+ if (!$this->connection) {
+ $this->connect();
+ }
+ if ($this->resultSet !== null) {
+ $this->resultSet = null;
+ }
+ if (!$this->autocommit) {
+ if ($this->transactionInError) {
+ return false;
+ }
+ else if (!$this->inTransaction) {
+ // No transaction yet started, let's begin one
+ $this->begin();
+ }
+ }
+ $preparedQuery = $this->prepareQuery($query, $params);
+
+ $error = null;
+
+ // returns number of changes to the database
+ if (@$this->connection->queryExec($preparedQuery, $error)) {
+ return @$this->connection->changes();
+ }
+
+ $this->rollback();
+ // XXX: Should perhaps pass some kind of SQL error state as code
+ throw new SqlException($error, $preparedQuery);
+ }
+
/**
* Returns the native database connection. Used internally by <code>SqlResultSet</code> which
* required the connection for some of its functionality.
=== modified file 'src/response/FileResponse.php'
--- src/response/FileResponse.php 2009-04-11 23:30:29 +0000
+++ src/response/FileResponse.php 2009-04-27 13:27:48 +0000
@@ -55,10 +55,12 @@
if ($this->dataIsFile) {
$this->setHeader('Content-Length', filesize($this->data));
+ $this->sendHeaders();
readfile($this->data);
}
else {
$this->setHeader('Content-Length', mb_strlen($this->data));
+ $this->sendHeaders();
echo $this->data;
}
}
=== modified file 'src/response/JsonResponse.php'
--- src/response/JsonResponse.php 2009-04-11 23:30:29 +0000
+++ src/response/JsonResponse.php 2009-04-27 13:27:48 +0000
@@ -18,6 +18,8 @@
* Performs the JSON encoding and outputs the result.
*/
public function render ($request) {
+ $this->sendHeaders();
+
echo json_encode($this->data);
}
}
=== modified file 'src/response/PhpResponse.php'
--- src/response/PhpResponse.php 2009-04-16 15:11:14 +0000
+++ src/response/PhpResponse.php 2009-04-27 13:27:48 +0000
@@ -32,6 +32,9 @@
* thus used as the results output.
*/
public function render ($request) {
+
+ $this->sendHeaders();
+
$data = array();
foreach ($this->data as $key => $value) {
$data[$key] = $value;
=== modified file 'src/response/RedirectResponse.php'
--- src/response/RedirectResponse.php 2009-04-14 16:50:50 +0000
+++ src/response/RedirectResponse.php 2009-04-27 13:27:48 +0000
@@ -5,7 +5,7 @@
* recommended to use a more specific response class where one is availible.
*/
class RedirectResponse extends Response {
- private $location;
+ public $location;
/**
* Initialize this response.
@@ -15,14 +15,17 @@
*/
public function __construct ($location, $status = 302) {
parent::__construct(null, $status);
- $this->location = Config::get('webRoot') . $location;
+ $this->location = $location;
}
/**
* Sends the redirect to the client.
*/
public function render ($request) {
- $this->setHeader('Location', $this->location);
+ $this->setHeader('Location', Config::get('webRoot') . $this->location);
+
+ $this->sendHeaders();
+
exit;
}
}
=== modified file 'src/response/Response.php' (properties changed: -x to +x)
--- src/response/Response.php 2009-04-12 16:32:29 +0000
+++ src/response/Response.php 2009-04-28 06:38:28 +0000
@@ -8,6 +8,7 @@
protected $status;
protected $contentType;
protected $charset;
+ protected $headerCache = array();
/**
* Initializes this response with the supplied response data and meta data. When using this
@@ -26,18 +27,17 @@
$this->status = $status;
$this->contentType = $contentType;
$this->charset = $charset;
- $this->setHeader('Content-Type', "$contentType; charset=$charset", $status);
+ $this->setHeader('Content-Type', "$contentType; charset=$charset");
}
/**
- * Sets a header. If $status is supplied, the HTTP status code is changed to its value.
+ * Sets a header in the headerCache array.
*
* @param string $header The header to set.
* @param string $value The value to set.
- * @param int $status New HTTP status code to set, if any.
*/
- public final function setHeader ($header, $value, $status = null) {
- header("$header: $value", true, $status);
+ public final function setHeader ($header, $value) {
+ $this->headerCache[] = "$header: $value";
}
/**
@@ -74,6 +74,16 @@
$this->data .= $content . "\n";
}
+
+ /**
+ * Sends out the buffered headers.
+ */
+ protected function sendHeaders() {
+ foreach($this->headerCache as $value) {
+ header($value, true, $this->status);
+ }
+ }
+
/**
* Outputs the response body.
*/
=== modified file 'src/response/SmartyResponse.php'
--- src/response/SmartyResponse.php 2009-04-11 23:30:29 +0000
+++ src/response/SmartyResponse.php 2009-04-27 13:27:48 +0000
@@ -44,6 +44,9 @@
* along with the request object and application configuration is exposed to the template.
*/
public function render ($request) {
+
+ $this->sendHeaders();
+
$conf = Config::get('smarty');
if ($conf === null) {
throw new Exception('Smarty settings missing from application configuration.');
=== modified file 'src/response/XsltResponse.php'
--- src/response/XsltResponse.php 2009-04-12 16:32:29 +0000
+++ src/response/XsltResponse.php 2009-04-27 13:27:48 +0000
@@ -25,6 +25,9 @@
* Generates XML of the data, and performs XSL transformation on it before outputting.
*/
public function render ($request) {
+
+ $this->sendHeaders();
+
$xmlGenerator = new XmlGenerator();
// Wrap request data in a containing element, <request />
$xmlGenerator->append($request, 'request');
@@ -41,6 +44,8 @@
}
echo $transformer->process($xmlGenerator->getDom());
+
+
}
}
?>
=== added directory 'src/specs'
=== added file 'src/specs/Fixtures.php'
--- src/specs/Fixtures.php 1970-01-01 00:00:00 +0000
+++ src/specs/Fixtures.php 2009-04-28 06:28:42 +0000
@@ -0,0 +1,64 @@
+<?php
+/**
+ * This class loads fixtures for our test-framework. It acts like an array, so you can
+ * access all your fixture by using <code>$this->fixture['fixtureName']</code>. Each
+ * fixture is loaded as their correct model, so you can call the method <code>save()</code>
+ * etc, when ever you like.
+ */
+class Fixtures implements ArrayAccess {
+ private $fixtures = array();
+ private $modelName = null;
+
+ /**
+ * Creates a new instance of this class. The modelName will be saved for usage in the
+ * <code>populate()</code> method wich is executed right away to set up the fixtures.
+ *
+ * @param string $modelName Name of the model.
+ */
+ public function __construct($modelName) {
+ $this->modelName = $modelName;
+ $this->populate();
+ }
+
+ /**
+ * Parses the <modelName>.ini file and returns an array of the objects.
+ */
+ public function populate () {
+ if ($this->modelName) {
+ $iniFile = APP_PATH . "/specs/fixtures/$this->modelName.ini";
+
+ if (!file_exists($iniFile)) {
+ return null;
+ }
+
+ $models = parse_ini_file($iniFile, true);
+ foreach ($models as $name => $model) {
+ if (is_array($model) && !empty($name)) {
+ $newModel = Models::init($this->modelName);
+
+ foreach ($model as $key => $value) {
+ $newModel->$key = $value;
+ }
+ $this->fixtures[$name] = $newModel;
+ }
+ }
+ }
+ }
+
+ /**
+ * Methods needed for ArrayAccess
+ */
+ public function offsetSet ($offset, $value) {
+ $this->fixtures[$offset] = $value;
+ }
+ public function offsetExists ($offset) {
+ return isset($this->fixtures[$offset]);
+ }
+ public function offsetUnset ($offset) {
+ unset($this->fixtures[$offset]);
+ }
+ public function offsetGet ($offset) {
+ return isset($this->fixtures[$offset]) ? $this->fixtures[$offset] : null;
+ }
+}
+?>
\ No newline at end of file
=== added file 'src/specs/KolibriContext.php'
--- src/specs/KolibriContext.php 1970-01-01 00:00:00 +0000
+++ src/specs/KolibriContext.php 2009-04-29 07:09:39 +0000
@@ -0,0 +1,138 @@
+<?php
+/**
+ * This class is the Kolibri Test framework. It serves as an class for Model, Action and View
+ * testing. Right now it support Action and Model testing. You have to extend KolibriContext
+ * to use this test-framework. It reflects the same methods as PHPSpec_Context has, but they
+ * are named differently. The corresponding method names are setup(), preSpec(), postSpec()
+ * and tearDown().
+ */
+class KolibriContext extends PHPSpec_Context {
+ public $fixtures;
+ public $modelName = null;
+ private $db = null;
+ private $testType;
+
+ const ACTION_TEST = 'action';
+ const VIEW_TEST = 'view';
+ const MODEL_TEST = 'model';
+
+ /**
+ * Executes before all spec methods are invoked. Distinguishes between model, action and view
+ * testing. It also establishes a database connection.
+ */
+ public function beforeAll () {
+ if (Config::getMode() != Config::TEST) {
+ throw new Exception("KolibriTestCase requires that the current KOLBRI_MODE is set to TEST.");
+ }
+
+ $className = get_class($this);
+
+ if (substr(strtolower($className), -5) == self::MODEL_TEST) {
+ $this->testType = self::MODEL_TEST;
+ $this->modelName = substr($className, 8, -5);
+ $this->fixtures = new Fixtures($this->modelName);
+ }
+ elseif (substr(strtolower($className), -6) == self::ACTION_TEST) {
+ $this->testType = self::ACTION_TEST;
+ }
+ elseif (substr(strtolower($className), -4) == self::VIEW_TEST) {
+ $this->testType = self::VIEW_TEST;
+ throw new Exception("KolibriContext does NOT support view testing yet.");
+ }
+ else {
+ throw new Exception("KolibriTestCase needs to have either Model, Action or View in the end of the classname");
+ }
+
+ $this->db = DatabaseFactory::getConnection();
+ if (method_exists($this, 'setup')) {
+ $this->setup();
+ }
+ }
+
+ /**
+ * Triggers the preSpec() method for doing something _before_ a spec has been invoked.
+ */
+ public function before () {
+ $this->db->begin();
+ if (method_exists($this, 'preSpec')) {
+ $this->preSpec();
+ }
+ }
+
+ /**
+ * Triggers the postSpec() method for doing something _after_ a spec. And it rolls back the current
+ * changes in the database.
+ */
+ public function after () {
+ $this->db->rollback();
+ if (method_exists($this, 'postSpec')) {
+ $this->postSpec();
+ }
+ }
+
+ /**
+ * Triggers the tearDown() method for doing something _after_ every spec has runned.
+ */
+ public function afterAll () {
+ unset($this->fixtures);
+ unset($this->modelName);
+
+ if (method_exists($this, 'tearDown')) {
+ $this->tearDown();
+ }
+
+ unset($this->db);
+
+ if (ob_get_level > 0) {
+ ob_flush();
+ }
+ }
+
+
+ /*
+ * Methods for Action testing
+ */
+ public function get ($uri, array $params = null, array $session = null) {
+ if ($this->validActionClass('get')) {
+ $this->prepareEnvironment('GET', $uri, $session);
+ $this->request = new Request($params !== null ? $params : array(), array());
+ $this->fireRequest($this->request);
+ }
+ }
+
+ public function post ($uri, array $params = null, array $session = null) {
+ if ($this->validActionClass('post')) {
+ $this->prepareEnvironment('POST', $uri, $session);
+ $this->request = new Request(array(), $params !== null ? $params : array());
+ $this->fireRequest($this->request);
+ }
+ }
+
+ private function fireRequest ($request) {
+ $rp = new RequestProcessor($request);
+ $this->response = $rp->process(false);
+ $this->action = $rp->getDispatcher()->getAction();
+ }
+
+ private function prepareEnvironment ($method, $uri, $session) {
+ $_SERVER['REQUEST_METHOD'] = $method;
+ $_SERVER['REQUEST_URI'] = $uri;
+ $_SESSION = $session !== null ? $session : array();
+ }
+
+ /**
+ * Does not allow you to use post and/or get in any other testing classes than action.
+ *
+ * @param string $method method that are tested for
+ * @return bool returns true if its allowed to be used
+ */
+ private function validActionClass ($method) {
+ if ($this->testType != self::ACTION_TEST){
+ throw new Exception("You are not allowed to use $method(), except in an action testing class.");
+ }
+ return true;
+ }
+
+}
+
+?>
\ No newline at end of file
=== added file 'src/specs/SpecHelper.php'
--- src/specs/SpecHelper.php 1970-01-01 00:00:00 +0000
+++ src/specs/SpecHelper.php 2009-04-28 06:38:28 +0000
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Loads all the correct files and mode for KolibriTestCase
+ *
+ * REMEMBER to require this file in every spec class you have
+ * <code>require_once(dirname(__FILE__) . '/../SpecHelper.php')</code>
+ *
+ * Defines the root directory of the Kolibri framework. By default this is a directory named
+ * 'kolibri' within the document root.
+ */
+if (!defined('ROOT')) {
+ if (!$rootDir = getenv('KOLIBRI_ROOT')) {
+ throw new Exception('Environment variable KOLIBRI_ROOT must be defined.');
+ exit;
+ }
+ define('ROOT', $rootDir);
+
+ /*
+ * Defines the root directory for the application. By default this is the same directory as
+ * this kolibri.php file.
+ */
+ $dirname = dirname(__FILE__);
+ if (basename($dirname) == 'specs') {
+ $path = dirname(__FILE__) . '/..';
+ }
+ else $path = dirname(__FILE__) . '/../..';
+
+ define('APP_PATH', $path);
+}
+
+putenv('KOLIBRI_MODE=test');
+require(ROOT . '/core/Config.php');
+require(ROOT . '/core/RequestProcessor.php');
+
+Config::getInstance();
+
+require(ROOT . '/specs/Fixtures.php');
+require(ROOT . '/specs/KolibriContext.php');
+
+$setupFile = APP_PATH . '/specs/setup.sql';
+$schemaFile = APP_PATH . '/config/schema.sql';
+
+if (file_exists($setupFile)) {
+ $db = DatabaseFactory::getConnection();
+
+ if (file_exists($schemaFile)) {
+ $schemaContents = file_get_contents($schemaFile);
+ $db->batchQuery($schemaContents);
+ $db->commit();
+ }
+
+ $setupContents = file_get_contents($setupFile);
+ $db->batchQuery($setupContents);
+ $db->commit();
+}
+?>
=== added file 'src/test.php'
--- src/test.php 1970-01-01 00:00:00 +0000
+++ src/test.php 2009-04-29 07:09:39 +0000
@@ -0,0 +1,30 @@
+<?php
+/**
+ * PHPSpec
+ * This file will run all specs within this directory and all child-directories.
+ * TODO safeguard sjekke at mode = test
+ */
+
+
+chdir('specs');
+
+require_once 'PHPSpec.php';
+
+/*
+ * We start the session here because the action tests are randomly executed
+ * and in some cases the session has not been initialized before the output.
+ */
+session_start();
+
+$options = new stdClass;
+$options->recursive = true;
+$options->specdoc = true;
+$options->reporter = 'html';
+
+ob_start();
+
+PHPSpec_Runner::run($options);
+
+ob_end_flush();
+
+?>
\ No newline at end of file
Follow ups