<?php

/*
 * This file is part of the sfLucenePlugin package
 * (c) 2007 - 2008 Carl Vondrick <*****@*****.**>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * @package sfLucenePlugin
 * @subpackage Test
 * @author Carl Vondrick
 * @version SVN: $Id: sfLuceneProjectConfigHandlerTest.php 7116 2008-01-20 22:43:28Z Carl.Vondrick $
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
$t = new limeade_test(2, limeade_output::get());
$limeade = new limeade_sf($t);
$app = $limeade->bootstrap();
$luceneade = new limeade_lucene($limeade);
$luceneade->configure()->clear_sandbox()->load_models();
$config = new sfLuceneProjectConfigHandler();
$response = $config->execute(array($luceneade->data_dir . '/configTest/project.yml'));
file_put_contents(lime_test::get_temp_directory() . '/search.yml.php', $response);
require lime_test::get_temp_directory() . '/search.yml.php';
unlink(lime_test::get_temp_directory() . '/search.yml.php');
$t->ok(isset($config), '->execute() creates a $config variable');
$t->is_deeply($config, array('testLucene' => array('models' => array('FakeModel' => array('fields' => array('id' => array('type' => 'text', 'boost' => 1, 'transform' => NULL), 'title' => array('type' => 'text', 'boost' => 1, 'transform' => NULL), 'description' => array('type' => 'text', 'boost' => 3, 'transform' => NULL)), 'title' => 'title', 'description' => 'description', 'categories' => array(0 => 'Forum'), 'route' => 'forum/showForum?id=%id%', 'validator' => 'isIndexable', 'rebuild_limit' => 5, 'peer' => 'FakeForumPeer', 'partial' => 'forumResult', 'indexer' => NULL)), 'index' => array('encoding' => 'UTF-8', 'cultures' => array(0 => 'en', 1 => 'fr'), 'stop_words' => array(0 => 'and', 1 => 'the'), 'short_words' => 2, 'analyzer' => 'utf8num', 'case_sensitive' => false, 'mb_string' => true, 'param' => array()), 'interface' => array('categories' => true, 'advanced' => true), 'factories' => array('indexers' => array(), 'results' => array())), 'barLucene' => array('models' => array('FakeModel' => array('fields' => array(), 'partial' => NULL, 'route' => NULL, 'indexer' => NULL, 'title' => NULL, 'description' => NULL, 'peer' => 'FakeModelPeer', 'rebuild_limit' => 250, 'validator' => NULL, 'categories' => array())), 'index' => array('encoding' => 'utf-8', 'cultures' => array(0 => NULL), 'stop_words' => array(0 => 'a', 1 => 'an', 2 => 'at', 3 => ' the', 4 => 'and', 5 => 'or', 6 => 'is', 7 => 'am', 8 => 'are', 9 => 'of'), 'short_words' => 2, 'analyzer' => 'textnum', 'case_sensitive' => false, 'mb_string' => false, 'param' => array()), 'factories' => array('indexers' => array(), 'results' => array())), 'fooLucene' => array('models' => array(), 'index' => array('encoding' => 'utf-8', 'cultures' => array(0 => NULL), 'stop_words' => array(0 => 'a', 1 => 'an', 2 => 'at', 3 => ' the', 4 => 'and', 5 => 'or', 6 => 'is', 7 => 'am', 8 => 'are', 9 => 'of'), 'short_words' => 2, 'analyzer' => 'textnum', 'case_sensitive' => false, 'mb_string' => false, 'param' => array()), 'factories' => array('indexers' => array(), 'results' => array()))), '->execute() writes the correct configuration from the YAML file');
<?php

/*
 * This file is part of the sfLucenePlugin package
 * (c) 2007 - 2008 Carl Vondrick <*****@*****.**>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
/**
 * @package sfLucenePlugin
 * @subpackage Test
 * @author Carl Vondrick
 * @version SVN: $Id$
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
$t = new limeade_test(2, limeade_output::get());
$config = new sfLuceneModuleConfigHandler();
$response = $config->execute(array(dirname(__FILE__) . '/../../fixtures/config/module.yml'));
file_put_contents(sys_get_temp_dir() . '/search.yml.php', $response);
require sys_get_temp_dir() . '/search.yml.php';
unlink(sys_get_temp_dir() . '/search.yml.php');
$t->ok(isset($actions), '->execute() creates a $actions variable');
$t->is_deeply($actions, array('testLucene' => array('bar' => array('security' => array('authenticated' => false, 'credentials' => array()), 'params' => array(), 'layout' => false), 'foo' => array('security' => array('authenticated' => true, 'credentials' => array('admin')), 'params' => array('foo' => 'bar', 'baz' => 'foobar'), 'layout' => true))), '->execute() writes the correct configuration from the YAML file');
$t->isa_ok(sfLuceneResult::getInstance($mockresult, $lucene), 'sfLuceneActionResult', '::getInstance() returns an instance of sfLuceneActionResult for "type" = action');
$mockresult->sfl_type = 'model';
$t->isa_ok(sfLuceneResult::getInstance($mockresult, $lucene), 'sfLuceneDoctrineResult', '::getInstance() returns an instance of sfLuceneModelResult for "type" = model');
$mockresult->sfl_type = 'regular';
$result = sfLuceneResult::getInstance($mockresult, $lucene);
$t->isa_ok($result, 'sfLuceneResult', '::getInstance() returns an instance of sfLuceneResult for "type" = regular');
$t->diag('testing ->getSearch(), ->getResult()');
$t->is($result->getSearch(), $lucene, '->getSearch() returns the same instance of sfLucene as initialized with');
$t->is($result->getResult(), $mockresult, '->getResult() returns the same instace of the result as initialized with');
$t->diag('testing simple ->get*()');
$t->is($result->getScore(), 43, '->getScore() gets the percentage from decimal and rounds');
$t->is($result->getInternalPartial(), 'sfLucene/regularResult', '->getInternalPartial() returns the correct partial name');
$t->diag('testing dynamic ->getXXX()');
$mockresult->sequence = '123';
$t->is($result->getSequence(), '123', '->getXXX() returns property XXX on document');
$t->ok($result->hasSequence(), '->hasXXX() returns true if document has property XXX');
$mockresult->super_duper_man = 'Fabien Potencier';
$t->is($result->getSuperDuperMan(), 'Fabien Potencier', '->getXXX() returns property XXX for camel case');
$t->ok($result->hasSuperDuperMan(), '->hasXXX() returns if document has property XXX for camel case');
try {
    $ex = $t->exception('->getXXX() fails if the property does not exist');
    $result->getSomethingReallyBad();
    $ex->no();
} catch (Exception $e) {
    $ex->caught($e);
}
$t->ok(!$result->hasSomethingReallyBad(), '->hasXXX() returns false if the document does not have property XXX');
$mockresult->sfl_field = '987';
$t->is($result->getInternalField(), '987', '->getInternalXXX() returns internal properties');
$t->ok($result->hasInternalField(), '->hasInternalXXX() returns true if internal property exists');
$t->diag('testing ->getInternalDescription()');
/**
 * @package sfLucenePlugin
 * @subpackage Test
 * @author Carl Vondrick
 * @version SVN: $Id: sfLuceneEventConnectorTest.php 7108 2008-01-20 07:44:42Z Carl.Vondrick $
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
$t = new limeade_test(5, limeade_output::get());
$limeade = new limeade_sf($t);
$app = $limeade->bootstrap();
class FooListener
{
    public $event;
    public function listen($event)
    {
        $this->event = $event;
    }
}
$source = new sfEventDispatcher();
$target = new sfEventDispatcher();
$connector = new sfLuceneEventConnector($source, 'foo', $target, 'bar');
$t->ok($source->hasListeners('foo'), '__construct() connects a listener to the source');
$subject = 'Fabien';
$params = array('a', 'b', 'c');
$listener = new FooListener();
$target->connect('bar', array($listener, 'listen'));
$source->notify(new sfEvent($subject, 'foo', $params));
$t->isa_ok($listener->event, 'sfEvent', 'calling a linked event calls target');
$t->is($listener->event->getSubject(), $subject, 'calling a linked event sends correct subject');
$t->is($listener->event->getName(), 'bar', 'calling a linked event sends correct name');
$t->is($listener->event->getParameters(), $params, 'calling a linked event sends correct parameters');
 * @author Carl Vondrick
 * @version SVN: $Id$
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
$t = new limeade_test(34, limeade_output::get());
class Foo
{
}
function inst()
{
    return sfLuceneCriteria::newInstance();
}
$t->isa_ok(sfLuceneCriteria::newInstance(), 'sfLuceneCriteria', '::newInstance() returns an sfLuceneCriteria object');
$criteria = inst();
$t->diag('testing ->getQuery()');
$t->ok(is_string($criteria->getQuery()), '->getQuery() returns an instance a string');
$t->diag('testing ->add()');
$criteria->add('test', sfLuceneCriteria::TYPE_AND);
$t->pass('->add() accepts a string');
$queries = inst()->add('foo')->add('bar')->getQuery();
$t->cmp_ok($queries, '===', 'foo AND bar', '->add() correctly parses and adds text queries');
$queries = inst()->add('foo')->add('bar', sfLuceneCriteria::TYPE_OR)->getQuery();
$t->cmp_ok($queries, '===', 'foo OR bar', '->add() correctly parses and adds text queries');
$query = inst();
$query->add('foo');
$criteria->add($query, null);
$t->pass('->add() accepts sfLuceneCriteria');
$luceneQuery = inst()->add($query);
$luceneQuery->add('bar', sfLuceneCriteria::TYPE_OR);
$subqueries = inst()->add($luceneQuery)->getQuery();
$t->cmp_ok($subqueries, '==', '((foo) OR bar)', '->getQuery() correctly combines sfLuceneCriteria queries');
try {
    $results->setPage(2);
    $t->pass('->setPage() accepts a integer page');
} catch (Exception $e) {
    $t->fail('->setPage() accepts a integer page');
}
try {
    $results->setMaxPerPage(10);
    $t->pass('->setMaxPerPage() accepts an integer per page');
} catch (Exception $e) {
    $t->fail('->setMaxPerPage() accepts an integer per page');
}
$t->is($results->getPage(), 2, '->getPage() returns current page');
$t->is($results->getMaxPerPage(), 10, '->getMaxPerPage() returns the max per page');
$t->is($results->getNbResults(), 1001, '->getNbResults() returns the total number of results');
$t->ok($results->haveToPaginate(), '->haveToPaginate() returns correct value');
$results->setPage(0);
$t->is($results->getPage(), 1, '->setPage() to 0 sets the page to 1');
$results->setPage(100000);
$t->is($results->getPage(), 101, '->setPage() above to upper bound resets to last page');
$results->setPage(2);
$t->diag('testing ->getResults()');
$t->is_deeply($results->getResults()->toArray(), range(10, 20), '->getResults() returns the correct range');
$results->setPage(3);
$t->is_deeply($results->getResults()->toArray(), range(20, 30), '->getResults() returns the correct range after page change');
$results->setMaxPerPage(0);
$t->is_deeply($results->getResults()->toArray(), range(0, 1000), '->getResults() returns all results if the max per page is 0');
$results->setMaxPerPage(10);
$t->diag('testing page numbers');
$t->is($results->getFirstPage(), 1, '->getFirstPage() returns 1 as first page');
$t->is($results->getLastPage(), 101, '->getLastPage() returns the last page in the range');
$t->diag('testing constructors');
try {
    $criteria = new sfLuceneCriteria(sfLucene::getInstance('testLucene'));
    $t->pass('__construct() takes a sfLucene instance');
} catch (Exception $e) {
    $t->fail('__construct() takes a sfLuce instance');
}
$t->isa_ok(sfLuceneCriteria::newInstance(sfLucene::getInstance('testLucene')), 'sfLuceneCriteria', '::newInstance() returns an sfLuceneCriteria object');
$t->diag('testing ->getQuery()');
$t->isa_ok($criteria->getQuery(), 'Zend_Search_Lucene_Search_Query_Boolean', '->getQuery() returns an instance of ZSL_Search_Query_Boolean');
$t->diag('testing ->add()');
try {
    $criteria->add('test', true);
    $t->pass('->add() accepts a string');
    $queries = inst()->add('foo')->add('bar')->getQuery()->getSubqueries();
    $t->ok($queries[0] == Zend_Search_Lucene_Search_QueryParser::parse('foo'), '->add() correctly parses and adds text queries');
    $t->ok($queries[1] == Zend_Search_Lucene_Search_QueryParser::parse('bar'), '->add() correctly parses and adds text queries and keeps them in order');
} catch (Exception $e) {
    $e->printStackTrace();
    $t->fail('->add() accepts a string');
    $t->skip('->add() correctly parses and adds text queries');
    $t->skip('->add() correctly parses and adds text queries and keeps them in order');
}
try {
    $criteria->add(new Zend_Search_Lucene_Search_Query_Boolean(), false);
    $t->pass('->add() accepts a Zend query');
    $query = new Zend_Search_Lucene_Search_Query_MultiTerm();
    $query->addTerm(new Zend_Search_Lucene_Index_Term('word1'), true);
    $query->addTerm(new Zend_Search_Lucene_Index_Term('word2'), null);
    $query->addTerm(new Zend_Search_Lucene_Index_Term('word3'), false);
    $queries = inst()->add($query)->getQuery()->getSubqueries();
try {
    $pager->setPage(2);
    $t->pass('->setPage() accepts a integer page');
} catch (Exception $e) {
    $t->fail('->setPage() accepts a integer page');
}
try {
    $pager->setMaxPerPage(10);
    $t->pass('->setMaxPerPage() accepts an integer per page');
} catch (Exception $e) {
    $t->fail('->setMaxPerPage() accepts an integer per page');
}
$t->is($pager->getPage(), 2, '->getPage() returns current page');
$t->is($pager->getMaxPerPage(), 10, '->getMaxPerPage() returns the max per page');
$t->is($pager->getNbResults(), 1001, '->getNbResults() returns the total number of results');
$t->ok($pager->haveToPaginate(), '->haveToPaginate() returns correct value');
$pager->setPage(0);
$t->is($pager->getPage(), 1, '->setPage() to 0 sets the page to 1');
$pager->setPage(100000);
$t->is($pager->getPage(), 101, '->setPage() above to upper bound resets to last page');
$pager->setPage(2);
$t->diag('testing ->getResults()');
$results = $pager->getResults()->toArray();
foreach (range(10, 19) as $id) {
    $guid = 'GUID_' . $id;
    $t->cmp_ok($results[$id - 10]->sfl_guid, '==', $guid, '->getResults() returns the correct range, sfl_guid:' . $guid);
}
$pager->setPage(3);
$results = $pager->getResults()->toArray();
foreach (range(20, 29) as $id) {
    $guid = 'GUID_' . $id;
$t->diag('testing __construct');
try {
    new sfLuceneCategory('foo', 'bar');
    $t->fail('__construct() must reject invalid holders');
} catch (Exception $e) {
    $t->pass('__construct() must reject invalid holders');
}
try {
    $c = new sfLuceneCategory($holder, 'bar', 5);
    $t->pass('__construct() must accept valid holders');
} catch (Exception $e) {
    $t->fail('__construct() must accept valid holders');
}
$t->diag('testing initialization parameters');
$t->is($c->getCount(), 5, '->getCount() returns default count');
$t->is($c->getName(), 'bar', '->getName() returns the correct name');
$t->ok($c->getHolder() === $holder, '->getHolder() returns the same holder');
$t->diag('testing ->add() and ->subtract()');
$t->is($c->add()->getCount(), 6, '->add() adds one to the count');
$t->is($c->add(5)->getCount(), 11, '->add() can add more than one to the count');
$t->is($c->subtract()->getCount(), 10, '->subtract() subtracts one from the count');
$t->is($c->subtract(3)->getCount(), 7, '->subtract() can subtract more than one from the count');
$t->is($c->setCount(0)->getCount(), 0, '->setCount() can explicitly change the count');
$t->ok($holder->isModified(), 'changing the count flags the holder for modication');
$t->diag('testing saving methods');
$t->ok(!$c->worthSaving(), '->worthSaving() returns false if the count is 0');
$c->setCount(8);
$t->ok($c->worthSaving(), '->worthSaving() returns true if the count is greater than 0');
$t->is($c->getPhp(), '$categories[\'bar\'] = 8;', '->getPhp() returns valid PHP to save');
$t->diag('testing magic methods');
$t->is($c->__toString(), 'bar', '__toString() returns the name');
Beispiel #10
0
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
$t = new limeade_test(11, limeade_output::get());
$lucene = sfLucene::getInstance('index', 'en', $app_configuration);
$luke = new sfLuceneLuke($lucene);
$t->cmp_ok($luke->getRequestHandlerUrl(), '===', 'http://localhost:8983/solr/index_en/admin/luke', '::getRequestUrl() ok');
if ($lucene->getSearchService()->ping()) {
    $t->diag('Solr available');
    $luke->loadInformation();
    $t->cmp_ok($luke->getNumDocs(), '===', 3, '::getNumDocs() ok');
    $t->cmp_ok($luke->getMaxDoc(), '>', 0, '::getMaxDoc() ok');
    $t->cmp_ok($luke->getNumTerms(), '===', 38, '::getNumTerms() ok');
    $t->cmp_ok(date("U", $luke->getVersion()), '!==', false, '::getVersion() ok');
    $t->cmp_ok($luke->getOptimized(), '===', true, '::getOptimized() ok');
    $t->cmp_ok($luke->getCurrent(), '===', true, '::getCurrent() ok');
    $t->ok(is_bool($luke->getHasDeletions()), '::getHasDeletions() ok');
    $t->ok(is_string($luke->getDirectory()), '::getDirectory() ok');
    $t->ok(strtotime($luke->getLastModified()), '::getLastModified() ok');
    $t->cmp_ok($luke->getStats('prout', 'null'), '===', null, '::getStats() ok');
} else {
    $t->skip('::getNumDocs()');
    $t->skip('::getMaxDoc()');
    $t->skip('::getNumTerms()');
    $t->skip('::getVersion()');
    $t->skip('::getOptimized()');
    $t->skip('::getCurrent()');
    $t->skip('::getHasDeletions()');
    $t->skip('::getDirectory()');
    $t->skip('::getLastModified()');
    $t->skip('::getStats()');
}
$t->like_included('#/Zend/Search/Lucene/#', '::loadZend() loads Zend Search Lucene');
$t->in_include_path('Zend/Search/Lucene.php', '::loadZend() configures include path');
$t->diag('testing ::getDirtyIndexRemains()');
$luceneade->clear_sandbox();
$root = sfConfig::get('sf_data_dir') . '/index/';
// build valid indexes structure
sfLucene::getInstance('testLucene', 'en')->getLucene();
sfLucene::getInstance('testLucene', 'fr')->getLucene();
// build invalid indexes structures
file_put_contents($root . 'testLucene/en/random_file', 'r@nd()');
mkdir($root . 'testLucene/foo', 0777, true);
file_put_contents($root . 'testLucene/foo/bar', 'foo');
mkdir($root . 'badIndex/en', 0777, true);
file_put_contents($root . 'badIndex/bar', 'foo');
$dirty = sfLuceneToolkit::getDirtyIndexRemains();
$t->ok(in_array($root . 'testLucene/foo', $dirty), '::getDirtyIndexRemains() schedules valid indexes but invalid cultures for deletion');
$t->ok(in_array($root . 'badIndex', $dirty), '::getDirtyIndexRemains() schedules the entire of a bad index for deletion');
$t->ok(!in_array($root . 'testLucene', $dirty), '::getDirtyIndexRemains() did not schedule an entire valid index for deletion');
$t->ok(!in_array($root . 'testLucene/en', $dirty), '::getDirtyIndexRemains() did not schedule a valid index and valid culture for deletion');
$t->ok(!in_array($root . 'testLucene/fr', $dirty), '::getDirtyIndexRemains() did not schedule another valid index and valid culture for deletion');
$t->ok(!in_array($root . 'testLucene/en/random_file', $dirty), '::getDirtyIndexRemains() did not schedule an alien file in a valid index and valid culture for deletion');
$t->diag('testing ::getApplicationInstance');
$t->ok(sfLuceneToolkit::getApplicationInstance('en') === sfLucene::getInstance('testLucene', 'en'), '::getApplicationInstance() guesses the first index with no configuration parameter set');
sfConfig::set('app_lucene_index', 'fooLucene');
$t->ok(sfLuceneToolkit::getApplicationInstance('en') === sfLucene::getInstance('fooLucene', 'en'), '::getApplicationInstance() acknowledges manual override from app.yml');
$limeade->config()->remove('app_lucene_index');
$cswap = $app->cswap($luceneade->config_dir . '/search.yml')->write('<?php $config = array();');
try {
    $e = $t->exception('::getApplicationInstance() fails if search.yml is empty');
    sfLuceneToolkit::getApplicationInstance();
    $e->no();
    //  $t->fail('Solr is not running');  die();
}
$t->is($service->getHost(), '127.0.0.1', '->getHost() ok');
$t->is($service->getPort(), '8983', '->getPort() ok');
$t->is($service->getPath(), '/solr/', '->getPath() ok');
$service->setPath('/solr/index_fr/');
$t->is($service->getPath(), '/solr/index_fr/', '->setPath() ok');
try {
    $response = $service->deleteByQuery('non_existent_field:asd');
    $t->fail('::deleteByQuery refers to a non existent field');
} catch (Exception $e) {
    $t->pass('::deleteByQuery raise an error on non existent field');
}
$t->diag("search for rande, limit:2, offset:0");
$response = $service->search('rande', 0, 2);
$t->ok($response instanceof sfLuceneDocument, '::search returns Apache_Solr_Response object');
$t->cmp_ok($response->getHttpStatusMessage(), '===', 'OK', '::getHttpStatusMessage return OK');
$t->cmp_ok($response->getHttpStatus(), '===', '200', '::getHttpStatus return code 200');
$t->cmp_ok($response->response->numFound, '===', 3, '->response->numFound return 3 entries');
$t->cmp_ok(count($response->response->docs), '===', 2, '->response->numFound return 2 entries');
$t->ok($response->response->docs[0] instanceof sfLuceneDocument, '->response->docs[0] return an instance sfLuceneDocument');
$t->cmp_ok($response->response->docs[0]->sfl_guid, '===', 'GUID_1', '->response->docs[0]->sfl_guid ok');
$t->cmp_ok($response->response->docs[1]->sfl_guid, '===', 'GUID_2', '->response->docs[1]->sfl_guid ok');
//
$t->diag("search for rande, limit:1, offset:2");
$response = $service->search('rande', 2, 1);
$t->ok($response instanceof sfLuceneDocument, '::search returns Apache_Solr_Response object');
$t->cmp_ok($response->getHttpStatusMessage(), '===', 'OK', '::getHttpStatusMessage return OK');
$t->cmp_ok($response->getHttpStatus(), '===', '200', '::getHttpStatus return code 200');
$t->cmp_ok($response->response->numFound, '===', 3, '->response->numFound return 3 entries');
$t->cmp_ok(count($response->response->docs), '===', 1, '->response->numFound return 2 entries');
Beispiel #13
0
$t->is($m->get('description'), 'description', 'model property "description" is the correct description field');
$t->is($m->get('categories'), array('Forum'), 'model property "categories" contains the correct categories');
$t->is($m->get('route'), 'forum/showForum?id=%id%', 'model property "route" is the correct route');
$t->is($m->get('validator'), 'isIndexable', 'model property "validator" is the correct validator');
$t->is($m->get('peer'), 'FakeForumPeer', 'model property "peer" is the correct peer');
$t->is($m->get('rebuild_limit'), 5, 'model property "rebuild_limit" is the correct rebuild limit');
$t->is($m->get('partial'), 'forumResult', 'model property "partial" is the correct partial');
$f = $m->get('fields');
$t->isa_ok($f, 'sfParameterHolder', 'model property "fields" is a sfParameterHolder');
$t->is($f->getNames(), array('id', 'title', 'description'), 'model property "fields" contains all the fields');
$t->is($f->get('id')->get('type'), 'unindexed', 'field property "type" is the type');
$t->is($f->get('id')->get('boost'), 1, 'field property "boost" is the boost');
$t->diag('testing ->getCategoriesHarness()');
$cats = $lucene->getCategoriesHarness();
$t->isa_ok($cats, 'sfLuceneCategories', '->getCategories() returns an instance of sfLuceneCategories');
$t->ok($lucene->getCategoriesHarness() === $cats, '->getCategories() is a singleton');
$t->diag('testing ->getIndexerFactory()');
$indexer = $lucene->getIndexerFactory();
$t->isa_ok($indexer, 'sfLuceneIndexerFactory', '->getIndexer() returns an instance of sfLuceneIndexerFactory');
$t->diag('testing ->configure()');
$lucene->configure();
$t->diag('testing ->find()');
class MockLucene
{
    public $args;
    public $scoring;
    public $e = false;
    public function search()
    {
        if ($this->e) {
            throw new Exception('Because you said so');
    $t->fail('__construct() accepts valid search instances and valid models');
}
$t->diag('testing ->insert(), fields');
$h->get('fields')->get('title')->set('type', 'foobar');
try {
    $indexer->insert();
    $t->fail('->insert() fails if a field has an invalid type');
} catch (Exception $e) {
    $t->pass('->insert() fails if a field has an invalid type');
}
$h->get('fields')->get('title')->set('type', 'keyword');
$indexer->delete();
$indexer->insert();
$lucene->commit();
$doc = getDoc($lucene, $indexer->getModelGuid());
$t->ok($doc->getDocument()->getField('title')->isStored, 'field type "Keyword" is stored');
$t->ok($doc->getDocument()->getField('title')->isIndexed, 'field type "Keyword" is indexed');
$t->ok(!$doc->getDocument()->getField('title')->isTokenized, 'field type "Keyword" is not tokenized');
$t->ok(!$doc->getDocument()->getField('title')->isBinary, 'field type "Keyword" is not binary');
$h->get('fields')->get('title')->set('type', 'unindexed');
$indexer->delete();
$indexer->insert();
$lucene->commit();
$doc = getDoc($lucene, $indexer->getModelGuid());
$t->ok($doc->getDocument()->getField('title')->isStored, 'field type "Unindexed" is stored');
$t->ok(!$doc->getDocument()->getField('title')->isIndexed, 'field type "Unindexed" is not indexed');
$t->ok(!$doc->getDocument()->getField('title')->isTokenized, 'field type "Unindexed" is not tokenized');
$t->ok(!$doc->getDocument()->getField('title')->isBinary, 'field type "Unindexed" is not binary');
$h->get('fields')->get('title')->set('type', 'binary');
$indexer->delete();
$indexer->insert();
        }
        throw new Exception('d');
    }
}
$data = array(new MockResult('foo'), new MockResult('bar'), new MockResult('baz'));
$search = sfLucene::getInstance('testLucene');
$results = new sfLuceneResults($data, $search);
$t->diag('testing ->getSearch(), ->toArray()');
$t->is($results->getSearch(), $search, '->getSearch() returns the same search instance');
$t->is($results->toArray(), $data, '->toArray() returns the same search data');
$t->diag('testing Iterator interface');
$got = array();
$once = false;
foreach ($results as $key => $value) {
    if (!$once) {
        $t->ok($value instanceof sfLuceneResult, 'iterator interface returns instances of sfLuceneResult');
        $once = true;
    }
    $got[$key] = $value->getResult();
}
$t->is($got, $data, 'sfLuceneResults implements the Iterator interface');
$t->diag('testing Countable interface');
$t->is(count($results), count($data), 'sfLuceneResults implements the Countable interface');
$t->diag('testing ArrayAccess interface');
$t->ok(isset($results[1]), 'sfLuceneResults implements the ArrayAccess isset() interface');
$t->ok($results[1] instanceof sfLuceneResult, 'sfLuceneResults ArrayAccess interface getter returns instances of sfLuceneResult');
$t->ok($results[1]->getResult(), 'sfLuceneResults implements the ArrayAccess getter interface');
$nresult = new MockResult('foobar');
$results[3] = $nresult;
$t->is($results[3]->getResult(), $nresult, 'sfLuceneResults implements the ArrayAccess setter interface');
unset($results[3]);
$app = $limeade->bootstrap();
$luceneade = new limeade_lucene($limeade);
$luceneade->configure()->clear_sandbox()->load_models();
$search = sfLucene::getInstance('testLucene');
$h = $search->getParameterHolder();
$t->diag('testing construct()');
try {
    $factory = new sfLuceneIndexerFactory($search);
    $t->pass('construct() accepts an instance of sfLucene');
} catch (Exception $e) {
    $t->fail('construct() accepts an instance of sfLucene');
}
$t->diag('testing ->getHandlers()');
$handlers = $factory->getHandlers();
$t->is(array_keys($handlers), array('model', 'action'), '->getHandlers() returns instances of the model and action handler by default');
$t->ok($handlers['model'] == new sfLucenePropelIndexerHandler($search), '->getHandlers() returns a valid model handler');
$t->ok($handlers['action'] == new sfLuceneActionIndexerHandler($search), '->getHandlers() returns a valid action handler');
$h->get('factories')->set('indexers', array('action' => array('Foo', 'FooIndexer')));
$handlers = $factory->getHandlers();
$t->ok($handlers['action'] == new Foo($search), '->getHandlers() can overload built-in handlers');
$h->get('factories')->set('indexers', array('action' => null));
$t->is(array_keys($factory->getHandlers()), array('model'), '->getHandlers() can eliminate handlers');
$h->get('factories')->set('indexers', array('pdf' => array('Foo', 'FooIndexer')));
$handlers = $factory->getHandlers();
$t->is(array_keys($handlers), array('model', 'action', 'pdf'), '->getHandlers() can add new handlers');
$t->ok($handlers['pdf'] == new Foo($search), '->getHandlers() can add new handlers correctly');
$t->diag('testing ->getModel()');
$model = new FakeForum();
$t->isa_ok($factory->getModel($model), 'sfLucenePropelIndexer', '->getModel() returns the Propel indexer by default');
$h->get('models')->get('FakeForum')->set('indexer', 'Foo');
$t->isa_ok($factory->getModel($model), 'Foo', '->getModel() can overload the indexer on the model level');
 */
/**
 * @package sfLucenePlugin
 * @subpackage Test
 * @author Carl Vondrick
 * @version SVN: $Id: sfLuceneStorageBlackholeTest.php 7108 2008-01-20 07:44:42Z Carl.Vondrick $
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
$t = new limeade_test(6, limeade_output::get());
$limeade = new limeade_sf($t);
$app = $limeade->bootstrap();
try {
    $bh = new sfLuceneStorageBlackhole('foo');
    $t->pass('__construct() accepts a string');
} catch (Exception $e) {
    $t->fail('__construct() accepts a string');
    $t->skip('the previous test must pass to continue');
    die;
}
$t->ok($bh instanceof sfLuceneStorage, 'sfLuceneStorageBlackhole implements sfLuceneStorage interface');
$t->is($bh->read(), null, '->read() is null initially');
try {
    $bh->write('foobar');
    $t->pass('->write() can write data');
    $t->is($bh->read(), 'foobar', '->read() reads the data written by ->write()');
} catch (Exception $e) {
    $t->fail('->write() can write data');
    $t->skip('->read() reads the data written by ->write()');
}
$bh->delete();
$t->is($bh->read(), null, '->delete() causes ->read() to return null');
    $t->fail('__construct() must reject invalid writers');
} catch (Exception $e) {
    $t->pass('__construct() must reject invalid writers');
}
try {
    $c = new sfLuceneCategories($lucene, $writer);
    $t->pass('__construct() must accept valid writers');
} catch (Exception $e) {
    $t->fail('__construct() must accept valid writers');
    $t->skip('the previous test must pass to continue');
    die;
}
$t->is($c->getAllCategories(), array(), '->getAllCategories() returns an empty array in the beginning');
$t->diag('testing ->getCategory()');
$category = $c->getCategory('foo');
$t->ok($category instanceof sfLuceneCategory, '->getCategory() returns an instance of sfLuceneCategory');
$t->is($category->getName(), 'foo', '->getCategory() returns a category with the correct name');
$t->is($category->getCount(), 0, '->getCategory() returns a category with a default score of 0');
$t->ok($category->getHolder() === $c, '->getCategory()->getHolder() returns the same instance as the holder');
$t->ok($category === $c->getCategory('foo'), '->getCategory() returns the same category each time, for a given name');
$t->diag('testing ->save()');
$category->add(10);
$t->ok($c->isModified(), 'modifying a category flags the holder for modification');
$c->save();
$t->is($writer->read(), '$categories = array();$categories[\'foo\'] = 10;', '->save() writes the changes to the writer');
$c->getCategory('bar')->add(2)->getHolder()->save();
$t->is($writer->read(), '$categories = array();$categories[\'foo\'] = 10;$categories[\'bar\'] = 2;', '->save() writes multiple changes to the writer');
$t->ok($c->isModified() == false, '->save() resets the modification flag');
$writer->write('foobarbaz');
$c->save();
$t->is($writer->read(), 'foobarbaz', '->save() does nothing if it is not modified');
$highlighter = new sfLuceneHighlighterMarkerDry();
try {
    $kw = new sfLuceneHighlighterKeywordNamed($highlighter, 'foobar');
    $t->pass('__construct() accepts a valid highlighter and valid name');
} catch (Exception $e) {
    $t->fail('__construct() accepts a valid highlighter and valid name');
}
$t->is($kw->getHighlighter(), $highlighter, '->getHighlighter() returns the correct highlighter');
$t->is($kw->getName(), 'foobar', '->getName() returns the correct name');
$t->is($kw->getLength(), 6, '->getLength() returns the correct length');
$got = $kw->tokenize('Foobar is my favorite foobar, but it needs the bar to be foobar');
$expected = array(new sfLuceneHighlighterToken($kw, 'foobar', 22, 28), new sfLuceneHighlighterToken($kw, 'foobar', 57, 63));
$t->diag('testing ->tokenize()');
$t->is($got, $expected, '->tokenize() returns correct positions for case-sensitivity');
$t->is($kw->tokenize('nothing interesting here.  move along!'), array(), '->tokenize() returns nothing if it does not appear in the string');
$t->ok($kw->tokenize('mr foobar, where are the foobars?') == array(new sfLuceneHighlighterToken($kw, 'foobar', 3, 9)), '->tokenize() only tokenizes exact matches');
$t->ok($kw->tokenize('foobar where art thou?') == array(new sfLuceneHighlighterToken($kw, 'foobar', 0, 6)), '->tokenize() can tokenize tokens in the very beginning');
$t->is($kw->tokenize('to be or not to be, that is the foobar'), array(new sfLuceneHighlighterToken($kw, 'foobar', 32, 38)), '->tokenize() can tokenize tokens in the very end');
$t->is($kw->tokenize('foobar'), array(new sfLuceneHighlighterToken($kw, 'foobar', 0, 6)), '->tokenize() can tokenize tokens in the very end and very beginning');
$t->is($kw->tokenize("\nfoobar\n"), array(new sfLuceneHighlighterToken($kw, 'foobar', 1, 7)), '->tokenize() can tokenize tokens with line returns around it');
$t->diag('testing ::generate()');
$lighters = array(new sfLuceneHighlighterMarkerUppercase(), new sfLuceneHighlighterMarkerSprint('[h]%s[/h]'));
$harness = new sfLuceneHighlighterMarkerHarness($lighters);
$keywords = sfLuceneHighlighterKeywordNamed::generate($harness, array('a', 'b', 'c'));
$t->is(count($keywords), 3, '::generate() returns the same number of initial keywords');
$t->ok($keywords[0]->getName() == 'a' && $keywords[1]->getName() == 'b' && $keywords[2]->getName() == 'c', '::generate() returns the keywords in the same order with the correct names');
$t->ok($keywords[0]->getHighlighter() === $lighters[0] && $keywords[1]->getHighlighter() == $lighters[1] && $keywords[2]->getHighlighter() == $lighters[0], '::generate() modulates on the same order as the harness');
$t->diag('testing ::explode()');
$keywords = sfLuceneHighlighterKeywordNamed::explode($harness, 'foobar António baz.....symf0ny');
$t->is(count($keywords), 4, '::explode() correctly splits the query into separate words');
$t->is($keywords[1]->getName(), 'António', '::explode() handles UTF8 strings correctly');
/**
 * @package sfLucenePlugin
 * @subpackage Test
 * @author Carl Vondrick
 * @version SVN: $Id: sfLuceneStorageFilesystemTest.php 7108 2008-01-20 07:44:42Z Carl.Vondrick $
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
clearstatcache();
// for some reason this unit test will go crazy without this...
$t = new limeade_test(12, limeade_output::get());
$limeade = new limeade_sf($t);
$app = $limeade->bootstrap();
$luceneade = new limeade_lucene($limeade);
$luceneade->configure()->clear_sandbox();
$file = $luceneade->sandbox_dir . '/storage/long/folder/tree';
$t->ok(!file_exists($file), 'target file does not exist initially');
try {
    $bh = new sfLuceneStorageFilesystem($file);
    $t->pass('__construct() accepts a file');
} catch (Exception $e) {
    $t->fail('__construct() accepts a file');
    $t->skip('the previous test must pass to continue');
    die;
}
$t->instanceof_ok($bh, 'sfLuceneStorage', 'sfLuceneStorageFilesystem implements sfLuceneStorage interface');
$t->is($bh->read(), null, '->read() is null initially');
try {
    $bh->write('foobar');
    $t->pass('->write() can write data');
    $t->is($bh->read(), 'foobar', '->read() reads the data written by ->write()');
} catch (Exception $e) {
 * @version SVN: $Id$
 */
require dirname(__FILE__) . '/../../../bootstrap/unit.php';
$t = new limeade_test(15, limeade_output::get());
$highlighter = new sfLuceneHighlighterMarkerDry();
try {
    $kw = new sfLuceneHighlighterKeywordNamedInsensitive($highlighter, 'FOOBAR');
    $t->pass('__construct() accepts a valid highlighter and valid name');
} catch (Exception $e) {
    $t->fail('__construct() accepts a valid highlighter and valid name');
}
$t->is($kw->getHighlighter(), $highlighter, '->getHighlighter() returns the correct highlighter');
$t->is($kw->getName(), 'FOOBAR', '->getName() returns the correct name');
$t->is($kw->getLength(), 6, '->getLength() returns the correct length');
$got = $kw->tokenize('Foobar is my favorite foobar, but it needs the bar to be foobar');
$expected = array(new sfLuceneHighlighterToken($kw, 'Foobar', 0, 6), new sfLuceneHighlighterToken($kw, 'foobar', 22, 28), new sfLuceneHighlighterToken($kw, 'foobar', 57, 63));
$t->is($got, $expected, '->tokenize() returns correct positions for case-insensitivity');
$t->is_deeply($kw->tokenize('nothing interesting here.  move along!'), array(), '->tokenize() returns nothing if it does not appear in the string');
$t->is($kw->tokenize('mr foobar, where are the foobars?'), array(new sfLuceneHighlighterToken($kw, 'foobar', 3, 9)), '->tokenize() only tokenizes exact matches');
$lighters = array(new sfLuceneHighlighterMarkerUppercase(), new sfLuceneHighlighterMarkerSprint('[h]%s[/h]'));
$harness = new sfLuceneHighlighterMarkerHarness($lighters);
$keywords = sfLuceneHighlighterKeywordNamedInsensitive::generate($harness, array('a', 'b', 'c'));
$t->is(count($keywords), 3, '::generate() returns the same number of initial keywords');
$t->ok($keywords[0]->getName() == 'a' && $keywords[1]->getName() == 'b' && $keywords[2]->getName() == 'c', '::generate() returns the keywords in the same order with the correct names');
$t->isa_ok($keywords[0], 'sfLuceneHighlighterKeywordNamedInsensitive', '::generate() returns instances of sfLuceneHighlighterKeywordNamedInsensitive');
$t->ok($keywords[0]->getHighlighter() === $lighters[0] && $keywords[1]->getHighlighter() == $lighters[1] && $keywords[2]->getHighlighter() == $lighters[0], '::generate() modulates on the same order as the harness');
$keywords = sfLuceneHighlighterKeywordNamedInsensitive::explode($harness, 'foobar António baz.....symf0ny');
$t->is(count($keywords), 4, '::explode() correctly splits the query into separate words');
$t->is($keywords[1]->getName(), 'António', '::explode() handles UTF8 strings correctly');
$t->is($keywords[2]->getName(), 'baz', '::explode() breaks correctly after a UTF8 string');
$t->is($keywords[3]->getName(), 'symf0ny', '::explode() breaks correctly after non-word but non-space character and handles numbers correctly');
 * @subpackage Test
 * @author Carl Vondrick
 * @version SVN: $Id$
 */
require dirname(__FILE__) . '/../../../bootstrap/unit.php';
$t = new limeade_test(3, limeade_output::get());
class Foo
{
    public $executed = false;
    public function execute()
    {
        $this->executed = true;
    }
}
$context = sfContext::createInstance($app_configuration);
$context->getResponse()->setContent('foobar 2357');
$filter = new sfLuceneRenderingFilter($context);
$chain = new Foo();
try {
    ob_start();
    $filter->execute($chain);
    $content = ob_get_clean();
    $t->pass('->execute() runs without an exception');
    $t->like($content, '/^foobar 2357.*/', '->execute() sends response content');
} catch (Exception $e) {
    ob_end_clean();
    $t->fail('->execute() runs without an exception');
    $t->skip('->execute() sends response content');
}
$t->ok($chain->executed, '->execute() runs ->execute() on the chain');
/**
 * @package sfLucenePlugin
 * @subpackage Test
 * @author Carl Vondrick
 * @version SVN: $Id: sfLuceneAdvancedFormTest.php 7108 2008-01-20 07:44:42Z Carl.Vondrick $
 */
require dirname(__FILE__) . '/../../bootstrap/unit.php';
$t = new limeade_test(10, limeade_output::get());
$limeade = new limeade_sf($t);
$app = $limeade->bootstrap();
$t->diag('testing constructor');
try {
    $form = new sfLuceneAdvancedForm();
    $t->pass('__construct() with no arguments does not throw an exception');
} catch (Exception $e) {
    $t->fail('__construct() with no arguments does not throw an exception');
}
$t->diag('testing initialization');
$t->isa_ok($form->getWidgetSchema(), 'sfWidgetFormSchema', 'widget schema is appropriate type');
$t->isa_ok($form->getValidatorSchema(), 'sfValidatorSchema', 'validator schema is appropriate type');
$t->isa_ok($form->getWidgetSchema()->getFormFormatter(), 'sfLuceneWidgetFormatterAdvanced', 'formatter is appropriate type');
$t->diag('testing categories');
$categories = array('foo', 'bar', 'baz', 'foobar');
$form->setCategories($categories);
$t->ok($form->getWidgetSchema()->offsetExists('category'), '->setCategories() adds "category" key to widget schema');
$t->is_deeply($form->getWidgetSchema()->offsetGet('category')->getOption('choices'), $categories, '->setCategories() configures widget with correct choices');
$t->ok($form->getValidatorSchema()->offsetExists('category'), '->setCategories() adds "category" key to validator schema');
$t->is_deeply($form->getValidatorSchema()->offsetGet('category')->getOption('choices'), $categories, '->setCategories() configures validator with correct choices');
$form->setCategories(array());
$t->ok(!$form->getWidgetSchema()->offsetExists('category'), '->setCategories() removes "category" key from widget schema');
$t->ok(!$form->getValidatorSchema()->offsetExists('category'), '->setCategories() removes "category" key from validator schema');
{
    public function getInternalPartial()
    {
        return 'FooPartial';
    }
}
class Bar
{
}
$foo = new Foo();
$bar = new Bar();
$t->diag('testing partial dependencies');
include_search_result($foo, 'query');
$values = include_partial(null, null, true);
$t->is($values['partial'], 'FooPartial', 'include_search_result() selects the correct partial');
$t->ok($values['params']['result'] === $foo, 'include_search_result() sends the same result');
$t->is($values['params']['query'], 'query', 'include_search_result() passes the query');
include_search_controls($foo);
$values = include_partial(null, null, true);
$t->is($values['partial'], 'sfLucene/controls', 'include_search_controls() selects the correct partial');
$t->ok($values['params']['form'] === $foo, 'include_search_controls() sends the same form');
include_search_pager($foo, $bar, 8);
$values = include_partial(null, null, true);
$t->is($values['partial'], 'sfLucene/pagerNavigation', 'include_search_pager() selects the correct partial');
$t->ok($values['params']['pager'] === $foo, 'include_search_pager() sends the same pager');
$t->ok($values['params']['form'] === $bar, 'include_search_pager() sends the same form');
$t->is($values['params']['radius'], 8, 'include_search_pager() sends the correct radius');
$t->diag('testing highlighting');
$t->is(highlight_result_text('Hello.  This is a pretty <em class="thing">awesome</em> thing to be talking about.', 'thing talking'), 'Hello.  This is a pretty awesome <strong class="highlight">thing</strong> to be <strong class="highlight">talking</strong> about.', 'highlight_result_text() highlights text and strips out HTML');
$t->is(highlight_result_text('Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. This is a pretty <em class="thing">awesome</em> thing to be talking about.  Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. Foo bar. ', 'thing talking', 50), '...is is a pretty awesome <strong class="highlight">thing</strong> to be <strong class="highlight">talking</strong> about....', 'highlight_result_text() highlights and truncates text');
$t->is(highlight_keywords('Hello.  This is a pretty <em class="thing">awesome</em> thing to be talking about.', 'thing talking'), 'Hello.  This is a pretty <em class="thing">awesome</em> <strong class="highlight">thing</strong> to be <strong class="highlight">talking</strong> about.', 'highlight_kewyords() highlights text');
 * file that was distributed with this source code.
 */
/**
 * @package sfLucenePlugin
 * @subpackage Test
 * @author Carl Vondrick
 * @version SVN: $Id: sfLuceneTest.php 7108 2008-01-20 07:44:42Z Carl.Vondrick $
 */
require dirname(__FILE__) . '/../bootstrap/unit.php';
$t = new limeade_test(91, limeade_output::get());
$limeade = new limeade_sf($t);
$app = $limeade->bootstrap();
$luceneade = new limeade_lucene($limeade);
$luceneade->configure()->clear_sandbox();
$t->diag('testing ::getInstance()');
$t->ok(!is_dir(sfConfig::get('sf_data_dir') . '/index/testLucene/en'), 'Lucene directory does not initially exist');
try {
    $e = $t->no_exception('::getInstance() allows valid cultures');
    $lucene = sfLucene::getInstance('testLucene', 'en');
    $e->no();
} catch (Exception $ex) {
    $e->caught($ex);
}
$t->ok(is_dir(sfConfig::get('sf_data_dir') . '/index/testLucene/en'), '::getInstance() creates the index');
$stat = stat(sfConfig::get('sf_data_dir') . '/index/testLucene/en/segments.gen');
$lucene->unlatch();
unset($lucene);
try {
    $lucene = sfLucene::getInstance('testLucene', 'en');
    clearstatcache();
    $t->is_deeply(stat(sfConfig::get('sf_data_dir') . '/index/testLucene/en/segments.gen'), $stat, '::getInstance() again opens the index');
        $this->saveQueue = array();
        $this->deleteQueue = array();
    }
}
$t->diag('testing ::getInitializer()');
$t->isa_ok(sfLucenePropelBehavior::getInitializer(), 'sfLucenePropelInitializer', '::getInitializer() returns an instance of sfLucenePropelInitializer');
$t->diag('testing ->getSearchInstances()');
$behavior = new MockBehavior();
try {
    $behavior->_getSearchInstances(new Foo());
    $t->fail('->getSearchInstances() fails if node cannot be found');
} catch (Exception $e) {
    $t->pass('->getSearchInstances() fails if node cannot be found');
}
$instances = $behavior->_getSearchInstances($m1);
$t->ok($instances === array(sfLucene::getInstance('testLucene', 'en'), sfLucene::getInstance('testLucene', 'fr')), '->getSearchInstances() returns all search instances for a Propel model');
$t->is($behavior->_getSearchInstances($m2), $instances, '->getSearchInstances() returns same instances for the same model');
$t->diag('testing ->preSave()');
$behavior->preSave($m1);
$q = $behavior->_getSaveQueue();
$t->is($q[0], $m1, '->preSave() adds model to queue if it does not already exist');
$t->is(count($q), 1, '->preSave() adds the model to queue only once');
$behavior->preSave($m1);
$t->is(count($behavior->_getSaveQueue()), 1, '->preSave() does not add model again if it already exists');
$behavior->preSave($m2);
$q = $behavior->_getSaveQueue();
$t->is($q[0], $m1, '->preSave() keeps unresolved models in queue');
$t->is($q[1], $m2, '->preSave() adds new models alongside old models');
$behavior->preSave($m3);
$q = $behavior->_getSaveQueue();
$t->is(count($q), 2, '->preSave() does not add unmodified objects to the queue');