/ * @todo add adapter testing */ class P4Cms_Categorization_Test extends TestCase { /** * Create dummy model records for testing. */ public function setUp() { parent::setUp(); // create a P4Cms_Record adapter $adapter = new P4Cms_Record_Adapter; $adapter->setConnection($this->p4) ->setBasePath("//depot"); P4Cms_Record::setDefaultAdapter($adapter); } /** * Remove dummy model records. */ public function tearDown() { // remove the P4Cms_Record adapter? P4Cms_Record::clearDefaultAdapter(); parent::tearDown(); } /** * Test idToFilespec. */ public function testIdToFilespec() { $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $tests = array( array( 'label' => __LINE__ .': category null', 'id' => null, 'error' => "Cannot get filespec for an empty id.", 'dirFilespec' => "//depot/folders/$index", 'friendFilespec' => "//depot/friends/$index", 'roundTripId' => '', ), array( 'label' => __LINE__ .': category empty', 'id' => '', 'error' => "Cannot get filespec for an empty id.", 'dirFilespec' => "//depot/folders/$index", 'friendFilespec' => "//depot/friends/$index", 'roundTripId' => '', ), array( 'label' => __LINE__ .': category number', 'id' => 123, 'error' => null, 'dirFilespec' => "//depot/folders/123/$index", 'friendFilespec' => "//depot/friends/123/$index", 'roundTripId' => '123', ), array( 'label' => __LINE__ .': category numeric', 'id' => '123', 'error' => null, 'dirFilespec' => "//depot/folders/123/$index", 'friendFilespec' => "//depot/friends/123/$index", 'roundTripId' => '123', ), array( 'label' => __LINE__ .': category alphanumeric', 'id' => 'abc123', 'error' => null, 'dirFilespec' => "//depot/folders/abc123/$index", 'friendFilespec' => "//depot/friends/abc123/$index", 'roundTripId' => 'abc123', ), array( 'label' => __LINE__ .': category *', 'id' => '*', 'error' => null, 'dirFilespec' => "//depot/folders/*/$index", 'friendFilespec' => "//depot/friends/*/$index", 'roundTripId' => '*', ), array( 'label' => __LINE__ .': category %%', 'id' => '%%', 'error' => null, 'dirFilespec' => "//depot/folders/%%/$index", 'friendFilespec' => "//depot/friends/%%/$index", 'roundTripId' => '%%', ), ); foreach ($tests as $test) { $label = $test['label']; $filespec = null; try { $filespec = P4Cms_Categorization_Dir::idToFilespec($test['id']); if (isset($test['error'])) { $this->fail("$label - unexpected dir success."); } } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { if (isset($test['error'])) { $this->assertSame( $test['error'], $e->getMessage(), "$label - expected dir error." ); } else { $this->fail("$label - unexpected dir exception:". $e->getMessage()); } } if (!isset($test['error'])) { $this->assertSame( $test['dirFilespec'], $filespec, "$label - expected dir filespec" ); $roundTripId = P4Cms_Categorization_Dir::depotFileToId($filespec); $this->assertSame( $test['roundTripId'], $roundTripId, "$label - expected dir roundTripId" ); } try { $filespec = P4Cms_Categorization_Friend::idToFilespec($test['id']); if (isset($test['error'])) { $this->fail("$label - unexpected friend success."); } } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { if (isset($test['error'])) { $this->assertSame( $test['error'], $e->getMessage(), "$label - expected friend error." ); } else { $this->fail("$label - unexpected friend exception:". $e->getMessage()); } } if (!isset($test['error'])) { $this->assertSame( $test['friendFilespec'], $filespec, "$label - expected friend filespec" ); $roundTripId = P4Cms_Categorization_Friend::depotFileToId($filespec); $this->assertSame( $test['roundTripId'], $roundTripId, "$label - expected dir roundTripId" ); } } } /** * Test nestingAllowed. */ public function testNestingAllowed() { $this->assertTrue(P4Cms_Categorization_Dir::isNestingAllowed(), 'dir should allow nesting'); $this->assertFalse(P4Cms_Categorization_Friend::isNestingAllowed(), 'friend should not allow nesting'); } /** * Test checkNestability. */ public function testCheckNestability() { // Dir should not cause an exception try { P4Cms_Categorization_Dir::callProtectedStaticFunc('_checkNestability'); } catch (Exception $e) { $this->fail( 'Unexpected exception for Dir (' . get_class($e) .') :'. $e->getMessage() ); } // Friend should cause an exception. try { P4Cms_Categorization_Friend::callProtectedStaticFunc('_checkNestability'); $this->fail("Unexpected success for Friend"); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (P4Cms_Categorization_Exception $e) { $this->assertSame( "This category does not permit nesting.", $e->getMessage(), "Expected error for Friend" ); } catch (Exception $e) { $this->fail( 'Unexpected exception for Friend (' . get_class($e) .') :'. $e->getMessage() ); } } /** * Test existence of categories and their entries. */ public function testExistence() { $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $this->assertFalse( P4Cms_Categorization_Dir::exists('adir'), 'adir should not exist.' ); $category = new P4Cms_Categorization_Dir; $category->setId('adir'); $this->assertFalse( $category->hasEntry('entry'), 'adir/entry should not exist.' ); $this->assertFalse( P4Cms_Categorization_Friend::exists('afriend'), 'afriend should not exist.' ); $encodedId = P4Cms_Categorization_Dir::encodeEntryId('entry'); $file = new P4_File; $file->setFilespec("//depot/folders/adir/$encodedId") ->add() ->setLocalContents('') ->submit('added adir/entry'); $category = new P4Cms_Categorization_Dir; $category->setId('adir'); $this->assertTrue( $category->hasEntry('entry'), 'adir/entry should now exist.' ); $this->assertFalse( P4Cms_Categorization_Dir::exists('adir'), 'adir should still not exist.' ); $file2 = new P4_File; $file2->setFilespec("//depot/folders/adir/$index") ->add() ->setLocalContents('') ->submit('added adir metadata'); $this->assertTrue( P4Cms_Categorization_Dir::exists('adir'), 'adir should now exist.' ); $file3 = new P4_File; $file3->setFilespec("//depot/friends/afriend/$index") ->add() ->setLocalContents('') ->submit('added afriend'); $this->assertTrue( P4Cms_Categorization_Friend::exists('afriend'), 'afriend should now exist.' ); } /** * Test setId. */ public function testSetId() { $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $tests = array( array( 'label' => __LINE__ .': null', 'id' => null, 'class' => 'P4Cms_Categorization_Dir', 'error' => null, ), array( 'label' => __LINE__ .': trailing underscore', 'id' => 'a_', 'class' => 'P4Cms_Categorization_Dir', 'error' => null, ), array( 'label' => __LINE__ .': leading period', 'id' => '.', 'class' => 'P4Cms_Categorization_Dir', 'error' => array( 'InvalidArgumentException' => 'Cannot set id: Leading periods are not permitted in category ids.', ), ), array( 'label' => __LINE__ .': leading dash', 'id' => '-', 'class' => 'P4Cms_Categorization_Dir', 'error' => array( 'InvalidArgumentException' => 'Cannot set id: Leading dashes are not permitted in category ids.', ), ), array( 'label' => __LINE__ .': trailing underscore', 'id' => 'a_', 'class' => 'P4Cms_Categorization_Dir', 'error' => null, ), array( 'label' => __LINE__ .': reserved index', 'id' => P4Cms_Categorization_CategoryAbstract::CATEGORY_FILENAME, 'class' => 'P4Cms_Categorization_Dir', 'error' => array( 'InvalidArgumentException' => 'Cannot set id: Id is reserved for internal use.', ), ), array( 'label' => __LINE__ .': / where nesting allowed', 'id' => 'a/b', 'class' => 'P4Cms_Categorization_Dir', 'error' => null, ), array( 'label' => __LINE__ .': / where nesting not allowed', 'id' => 'a/b', 'class' => 'P4Cms_Categorization_Friend', 'error' => array( 'P4Cms_Categorization_Exception' => 'This category does not permit nesting.', ), ), ); foreach ($tests as $test) { $label = $test['label']; $object = null; try { $object = new $test['class']; $object->setId($test['id']); if (isset($test['error'])) { $this->fail("$label - unexpected success"); } } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (Exception $e) { if (isset($test['error'])) { $error = each($test['error']); $this->assertSame( $error[0], get_class($e), "$label - Expected exception. Got (". get_class($e) .') :'. $e->getMessage() ); $this->assertSame( $error[1], $e->getMessage(), "$label - Expected error message." ); } else { $this->fail( "$label - Unexpected exception (" . get_class($e) .') :'. $e->getMessage() ); } } if (!isset($test['error'])) { $this->assertSame( $test['id'], $object->getId(), "$label - Expected id." ); } } } /** * Test remove. */ public function testRemove() { // prepare file layout $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $file1 = new P4_File; $file1->setFilespec("//depot/folders/adir/$index") ->add() ->setLocalContents('') ->submit('added adir metadata'); $this->assertTrue( P4Cms_Categorization_Dir::exists('adir'), 'adir should exist.' ); $encodedId = P4Cms_Categorization_Dir::encodeEntryId('entry'); $file2 = new P4_File; $file2->setFilespec("//depot/folders/adir/$encodedId") ->add() ->setLocalContents('') ->submit('added adir/entry'); $category = new P4Cms_Categorization_Dir; $category->setId('adir'); $this->assertTrue( $category->hasEntry('entry'), 'adir/entry should exist.' ); $file3 = new P4_File; $file3->setFilespec("//depot/folders/bdir/$index") ->add() ->setLocalContents('') ->submit('added bdir metadata'); $this->assertTrue( P4Cms_Categorization_Dir::exists('bdir'), 'bdir should exist.' ); $file4 = new P4_File; $file4->setFilespec("//depot/folders/bdir/$encodedId") ->add() ->setLocalContents('') ->submit('added bdir/entry'); $category = new P4Cms_Categorization_Dir; $category->setId('bdir'); $this->assertTrue( $category->hasEntry('entry'), 'bdir/entry should exist.' ); $file5 = new P4_File; $file5->setFilespec("//depot/folders/bdir/subdir/$index") ->add() ->setLocalContents('') ->submit('added bdir/subdir metadata'); $this->assertTrue( P4Cms_Categorization_Dir::exists('bdir/subdir'), 'bdir/subdir should exist.' ); $file6 = new P4_File; $file6->setFilespec("//depot/folders/bdir/subdir2/$index") ->add() ->setLocalContents('') ->submit('added bdir/subdir2 metadata'); $this->assertTrue( P4Cms_Categorization_Dir::exists('bdir/subdir2'), 'bdir/subdir2 should exist.' ); // try to delete a subdir $path = 'bdir/subdir'; try { P4Cms_Categorization_Dir::remove($path); } catch (Exception $e) { $this->fail( "Unexpected exception removing '$path' (" . get_class($e) .') :'. $e->getMessage() ); } $this->assertTrue( P4Cms_Categorization_Dir::exists('bdir'), 'bdir should exist.' ); $this->assertFalse( P4Cms_Categorization_Dir::exists('bdir/subdir'), 'bdir/subdir should no longer exist.' ); $this->assertTrue( P4Cms_Categorization_Dir::exists('bdir/subdir2'), 'bdir/subdir2 should still exist.' ); // try to delete adir $path = 'adir'; try { P4Cms_Categorization_Dir::remove($path); } catch (Exception $e) { $this->fail( "Unexpected exception removing '$path' (" . get_class($e) .') :'. $e->getMessage() ); } $this->assertFalse( P4Cms_Categorization_Dir::exists('adir'), 'adir should no longer exist.' ); $category = new P4Cms_Categorization_Dir; $category->setId('adir'); $this->assertFalse( $category->hasEntry('entry'), 'adir/entry should no longer exist.' ); // try to delete nested path $path = 'bdir'; try { P4Cms_Categorization_Dir::remove($path); } catch (Exception $e) { $this->fail( "Unexpected exception removing '$path' (" . get_class($e) .') :'. $e->getMessage() ); } $this->assertFalse( P4Cms_Categorization_Dir::exists('bdir'), 'bdir should no longer exist.' ); $category = new P4Cms_Categorization_Dir; $category->setId('bdir'); $this->assertFalse( $category->hasEntry('entry'), 'bdir/entry should no longer exist.' ); $this->assertFalse( P4Cms_Categorization_Dir::exists('bdir/subdir2'), 'bdir/subdir2 should no longer exist.' ); // try to delete again $path = 'bdir'; try { P4Cms_Categorization_Dir::remove($path); $this->fail("Unexpected success removing already removed path '$path'"); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (P4Cms_Categorization_Exception $e) { $this->assertSame( 'Cannot delete category. Category does not exist.', $e->getMessage(), "Expected error removing already removed path '$path'" ); } } /** * Test whether a category exists. */ public function testExists() { $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $this->assertFalse( P4Cms_Categorization_Dir::exists('dne'), 'dne in root dir should not exist.' ); // create an entry in each hierarchy for existence checks $file = new P4_File; $file->setFilespec("//depot/folders/newdir/$index") ->add() ->setLocalContents('') ->submit('added newdir'); $file2 = new P4_File; $file2->setFilespec('//depot/folders/newdir/newentry') ->add() ->setLocalContents('') ->submit('added newdir/newentry'); $file3 = new P4_File; $file3->setFilespec('//depot/friends/newfriend') ->add() ->setLocalContents('') ->submit('added newfriend'); // test updated existance $this->assertTrue( P4Cms_Categorization_Dir::exists('newdir'), 'newdir should exist.' ); $this->assertFalse( P4Cms_Categorization_Dir::exists('newdir/'), 'newdir/ should not exist.' ); // category content should NOT be identified with exists. $this->assertFalse( P4Cms_Categorization_Dir::exists('newdir/newentry'), 'newdir/newentry should exist.' ); $this->assertFalse( P4Cms_Categorization_Friend::exists('newfriend'), 'newfriend should exist.' ); } /** * Test hasChildren and getChildren */ public function testHasChildrenGetChildren() { $dir = new P4Cms_Categorization_Dir; $this->assertFalse( $dir->setId('nokids')->hasChildren(), 'nokids should have no children' ); $this->assertFalse( $dir->setId('somekids')->hasChildren(), 'somekids should have no children yet' ); $this->assertFalse( $dir->setId('somekids/anotherkid')->hasChildren(), 'somekids/anotherkid should have no children' ); // verify that friends do not have children $friend = new P4Cms_Categorization_Friend; try { $result = $friend->setId('bob')->hasChildren(); $this->fail('Unexpected success with hasChildren on friend.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (P4Cms_Categorization_Exception $e) { $this->assertSame( 'This category does not permit nesting.', $e->getMessage(), 'Expected error with hasChildren on friend.' ); } try { $children = $friend->getChildren(); $this->fail('Unexpected success with getChildren on friend.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (P4Cms_Categorization_Exception $e) { $this->assertSame( 'This category does not permit nesting.', $e->getMessage(), 'Expected error with getChildren on friend.' ); } // populate category $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $file = new P4_File; $file->setFilespec("//depot/folders/somekids/$index") ->add() ->setLocalContents('') ->submit("added somekids/$index"); $file2 = new P4_File; $file2->setFilespec("//depot/folders/somekids/anotherkid/$index") ->add() ->setLocalContents('') ->submit("added somekids/anotherkid/$index"); $file3 = new P4_File; $file3->setFilespec("//depot/folders/somekids/anotherkid/deeper/$index") ->add() ->setLocalContents('') ->submit('added somekids/anotherkid/deeper_index'); $file4 = new P4_File; $file4->setFilespec("//depot/folders/nokids/$index") ->add() ->setLocalContents('') ->submit("added nokids/$index"); $file5 = new P4_File; $file5->setFilespec("//depot/friends/bob/$index") ->add() ->setLocalContents('') ->submit("added bob/$index"); $file5 = new P4_File; $file5->setFilespec('//depot/friends/bob/invalid') ->add() ->setLocalContents('') ->submit('added bob/invalid'); // verify that friends still do not have children $friend = new P4Cms_Categorization_Friend; try { $result = $friend->setId('bob')->hasChildren(); $this->fail('Unexpected success with hasChildren on friend #2.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (P4Cms_Categorization_Exception $e) { $this->assertSame( 'This category does not permit nesting.', $e->getMessage(), 'Expected error with hasChildren on friend #2.' ); } try { $children = $friend->getChildren(); $this->fail('Unexpected success with getChildren on friend #2.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (P4Cms_Categorization_Exception $e) { $this->assertSame( 'This category does not permit nesting.', $e->getMessage(), 'Expected error with getChildren on friend #2.' ); } // get child categories non-recursively $ids = array(); $categories = $dir->setId('somekids')->getChildren(); foreach ($categories as $category) { $ids[] = $category->getId(); } $this->assertSame( array('somekids/anotherkid'), $ids, 'Expected children in somekids, non-recursive' ); // get child categories recursively $ids = array(); $categories = $dir->setId('somekids')->getChildren(true); foreach ($categories as $category) { $ids[] = $category->getId(); } $this->assertSame( array( 'somekids/anotherkid', 'somekids/anotherkid/deeper', ), $ids, 'Expected children in somekids, recursive' ); $this->assertFalse( $dir->setId('nokids')->hasChildren(), 'nokids should have no children' ); $this->assertTrue( $dir->setId('somekids')->hasChildren(), 'somekids should have children now' ); $this->assertTrue( $dir->setId('somekids/anotherkid')->hasChildren(), 'somekids/anotherkid should have children' ); $this->assertFalse( $dir->setId('somekids/anotherkid/deeper')->hasChildren(), 'somekids/anotherkid/deeper should have no children' ); } /** * Test hasParent() and getParent(). */ public function testHasParentGetParent() { // test with no id. $dir = new P4Cms_Categorization_Dir; $result = $dir->hasParent(); $this->assertFalse($dir->hasParent(), 'Category with no id should have no parent.'); try { $dir->getParent(); $this->fail('Unexpected success with getParent on root category.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (P4Cms_Categorization_Exception $e) { $this->assertSame( 'Cannot get parent. This category has no parent.', $e->getMessage(), 'Expected error with getParent on root category.' ); } // test with a variety of ids that do not (yet) exist. $testCategories = array( 'category', 'category/sub' ); foreach ($testCategories as $category) { $dir = new P4Cms_Categorization_Dir; $this->assertFalse( $dir->setId($category)->hasParent(), "Category '$category' should not have a parent." ); } $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $file = new P4_File; $file->setFilespec("//depot/folders/category/$index") ->add() ->setLocalContents('') ->submit("added category/$index"); $file2 = new P4_File; $file2->setFilespec("//depot/folders/category/sub/$index") ->add() ->setLocalContents('') ->submit("added category/sub/$index"); // test with ids that should now exist. $testCategories = array( 'category/sub' => 'category', 'category/sub2' => 'category', ); foreach ($testCategories as $category => $expected) { $dir = new P4Cms_Categorization_Dir; $this->assertTrue( $dir->setId($category)->hasParent(), "Expected '$category' to have a parent." ); $parent = $dir->getParent(); $this->assertSame($expected, $parent->getId(), "Expected parent id for '$category'"); } } /** * Test create and delete. */ public function testCreateDelete() { $query = P4_File_Query::create()->addFilespec('//depot/folders/...'); $files = P4_File::fetchAll($query); $this->assertEquals(0, count($files), 'Expect no categories at outset.'); $tests = array( array( 'label' => __LINE__ .': null', 'class' => 'P4Cms_Categorization_Dir', 'values' => array('id' => null), 'error' => array( 'P4Cms_Categorization_Exception' => 'Cannot save; category id is not set.' ), ), array( 'label' => __LINE__ .': empty string', 'class' => 'P4Cms_Categorization_Dir', 'values' => array('id' => null), 'error' => array( 'P4Cms_Categorization_Exception' => 'Cannot save; category id is not set.' ), ), array( 'label' => __LINE__ .': id with underscore', 'class' => 'P4Cms_Categorization_Dir', 'values' => array( 'id' => 'a_b', 'title' => 'a title', 'description' => 'a description', ), 'error' => null, ), array( 'label' => __LINE__ .': id with leading period', 'class' => 'P4Cms_Categorization_Dir', 'values' => array('id' => '.ab'), 'error' => array( 'InvalidArgumentException' => 'Cannot set id: Leading periods are not permitted in category ids.', ), ), array( 'label' => __LINE__ .': id matching CATEGORY_FILENAME', 'class' => 'P4Cms_Categorization_Dir', 'values' => array('id' => P4Cms_Categorization_CategoryAbstract::CATEGORY_FILENAME), 'error' => array( 'InvalidArgumentException' => 'Cannot set id: Id is reserved for internal use.', ), ), array( 'label' => __LINE__ .': invalid nesting', 'class' => 'P4Cms_Categorization_Friend', 'values' => array('id' => 'a/b'), 'error' => array( 'P4Cms_Categorization_Exception' => 'This category does not permit nesting.', ), ), array( 'label' => __LINE__ .': non-existant path', 'class' => 'P4Cms_Categorization_Dir', 'values' => array('id' => 'a/b'), 'error' => array( 'InvalidArgumentException' => 'Cannot create new category;' . ' category ancestry does not exist.', ), ), array( 'label' => __LINE__ .': success a', 'class' => 'P4Cms_Categorization_Dir', 'values' => array( 'id' => 'a', 'title' => 'a title', 'description' => 'a description', ), 'error' => null, ), array( 'label' => __LINE__ .': success a/b', 'class' => 'P4Cms_Categorization_Dir', 'values' => array( 'id' => 'a/b', 'title' => 'a/b title', 'description' => 'a/b description', ), 'error' => null, ), array( 'label' => __LINE__ .': success a/b/c', 'class' => 'P4Cms_Categorization_Dir', 'values' => array( 'id' => 'a/b/c', 'title' => 'a/b/c title', 'description' => 'a/b/c description', ), 'error' => null, ), array( 'label' => __LINE__ .': success a', 'class' => 'P4Cms_Categorization_Dir', 'values' => array( 'id' => 'a', 'title' => 'a title', 'description' => 'a description', ), ), ); foreach ($tests as $test) { $label = $test['label']; $category = null; try { $category = $test['class']::store($test['values']); if (isset($test['error'])) { $this->fail("$label - Unexpected success"); } } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (Exception $e) { if (isset($test['error'])) { $actual = array(get_class($e) => $e->getMessage()); $this->assertSame($test['error'], $actual, "$label - expected exception"); } else { $this->fail( "$label - Unexpected exception (" . get_class($e) .') :'. $e->getMessage() ); } } if (!isset($test['error'])) { $this->assertSame($test['values']['id'], $category->getId(), "$label - expected id"); foreach ($test['values'] as $key => $value) { $this->assertSame( $value, $category->getValue($key), "$label - expected value for '$key'" ); } } } // confirm files in depot $prefix = '//depot/folders'; $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $query = P4_File_Query::create()->addFilespec("$prefix/..."); $files = P4_File::fetchAll($query); $filespecs = array(); foreach ($files as $file) { $filespecs[] = $file->getFilespec(); } $this->assertSame( array( "$prefix/a/$index", "$prefix/a/b/$index", "$prefix/a/b/c/$index", "$prefix/a_b/$index", ), $filespecs, 'Expected categories after creation.' ); // test deletions of constructed categories $this->assertTrue( (bool)P4_File::exists("$prefix/a/b/c/$index", null, true), 'Expected third file to exist' ); $category = new P4Cms_Categorization_Dir; $category->setId('a/b/c')->delete(); $this->assertFalse( P4_File::exists("$prefix/a/b/c/$index", null, true), 'Expected third file to no longer exist' ); // try deleting a. $category = new P4Cms_Categorization_Dir; $category->setId('a'); $category->delete(); $this->assertFalse(P4_File::exists("$prefix/a/$index", null, true), 'Expected a to no longer exist'); $this->assertFalse(P4_File::exists("$prefix/a/b/$index", null, true), 'Expected a/b to no longer exist'); } /** * Test move. */ public function testMove() { // create some categories containing entries try { P4Cms_Categorization_Dir::store('zero'); P4Cms_Categorization_Dir::store('one'); P4Cms_Categorization_Dir::store('one/sub1'); $category = P4Cms_Categorization_Dir::store('two'); $category->addEntry('a'); $category = P4Cms_Categorization_Dir::store('three'); $category->addEntries(array('b', 'c')); $category = P4Cms_Categorization_Dir::store('three/sub3'); $category->addEntries(array('d')); } catch (Exception $e) { $this->fail( 'Unexpected exception creating categories for move testing (' . get_class($e) .') '. $e->getMessage() ); } $prefix = '//depot/folders'; $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $encodedA = P4Cms_Categorization_Dir::encodeEntryId('a'); $encodedB = P4Cms_Categorization_Dir::encodeEntryId('b'); $encodedC = P4Cms_Categorization_Dir::encodeEntryId('c'); $encodedD = P4Cms_Categorization_Dir::encodeEntryId('d'); $query = P4_File_Query::create()->addFilespec("$prefix/..."); $files = P4_File::fetchAll($query); $filespecs = array(); foreach ($files as $file) { $filespecs[] = $file->getFilespec(); } $this->assertSame( array( "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zero/$index", ), $filespecs, "Expected initial category layout." ); $tests = array( array( 'label' => __LINE__ .': null source and target categories', 'sourceId' => null, 'targetId' => null, 'error' => 'Cannot move category; both the source and target category must be specified.', 'expect' => null, ), array( 'label' => __LINE__ .': null source category', 'sourceId' => null, 'targetId' => 'bogus', 'error' => 'Cannot move category; both the source and target category must be specified.', 'expect' => null, ), array( 'label' => __LINE__ .': null target category', 'sourceId' => 'bogus', 'targetId' => null, 'error' => 'Cannot move category; both the source and target category must be specified.', 'expect' => null, ), array( 'label' => __LINE__ .': root source category', 'sourceId' => '', 'targetId' => 'one', 'error' => 'Cannot move category; neither the source or target category can be "".', 'expect' => null, ), array( 'label' => __LINE__ .': root target category', 'sourceId' => 'one', 'targetId' => '', 'error' => 'Cannot move category; neither the source or target category can be "".', 'expect' => null, ), array( 'label' => __LINE__ .': non-existant source category', 'sourceId' => 'bogus', 'targetId' => 'bogus2', 'error' => 'Cannot move category; source category does not exist.', 'expect' => null, ), array( 'label' => __LINE__ .': source category == target category', 'sourceId' => 'one', 'targetId' => 'one', 'error' => 'Cannot move category; target category already exists.', 'expect' => null, ), array( 'label' => __LINE__ .': target category exists', 'sourceId' => 'one', 'targetId' => 'two', 'error' => 'Cannot move category; target category already exists.', 'expect' => null, ), array( 'label' => __LINE__ .': target category is subcat of source', 'sourceId' => 'three', 'targetId' => 'three/bogus', 'error' => 'Cannot move category; target category is within source category.', 'expect' => null, ), array( 'label' => __LINE__ .': move category with no contents', 'sourceId' => 'zero', 'targetId' => 'moved', 'error' => null, 'expect' => array( "$prefix/moved/$index", "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", ), ), array( 'label' => __LINE__ .': move back category with no contents', 'sourceId' => 'moved', 'targetId' => 'zero', 'error' => null, 'expect' => array( "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zero/$index", ), ), array( 'label' => __LINE__ .': move a category with target id starts with source id', 'sourceId' => 'zero', 'targetId' => 'zeroOne', 'error' => null, 'expect' => array( "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zeroOne/$index", ), ), array( 'label' => __LINE__ .': move a category with source id starts with target id', 'sourceId' => 'zeroOne', 'targetId' => 'zero', 'error' => null, 'expect' => array( "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zero/$index", ), ), array( 'label' => __LINE__ .': move category with entries', 'sourceId' => 'two', 'targetId' => 'moved', 'error' => null, 'expect' => array( "$prefix/moved/$index", "$prefix/moved/$encodedA", "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/zero/$index", ), ), array( 'label' => __LINE__ .': move back category with entries', 'sourceId' => 'moved', 'targetId' => 'two', 'error' => null, 'expect' => array( "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zero/$index", ), ), array( 'label' => __LINE__ .': move category with entries and subcats', 'sourceId' => 'three', 'targetId' => 'moved', 'error' => null, 'expect' => array( "$prefix/moved/$index", "$prefix/moved/$encodedB", "$prefix/moved/$encodedC", "$prefix/moved/sub3/$index", "$prefix/moved/sub3/$encodedD", "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zero/$index", ), ), array( 'label' => __LINE__ .': move back category with entries and subcats', 'sourceId' => 'moved', 'targetId' => 'three', 'error' => null, 'expect' => array( "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zero/$index", ), ), array( 'label' => __LINE__ .': move category to subcat', 'sourceId' => 'three', 'targetId' => 'one/three', 'error' => null, 'expect' => array( "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/one/three/$index", "$prefix/one/three/$encodedB", "$prefix/one/three/$encodedC", "$prefix/one/three/sub3/$index", "$prefix/one/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zero/$index", ), ), array( 'label' => __LINE__ .': move subcat back to category', 'sourceId' => 'one/three', 'targetId' => 'three', 'error' => null, 'expect' => array( "$prefix/one/$index", "$prefix/one/sub1/$index", "$prefix/three/$index", "$prefix/three/$encodedB", "$prefix/three/$encodedC", "$prefix/three/sub3/$index", "$prefix/three/sub3/$encodedD", "$prefix/two/$index", "$prefix/two/$encodedA", "$prefix/zero/$index", ), ), ); foreach ($tests as $test) { $label = $test['label']; try { P4Cms_Categorization_Dir::move($test['sourceId'], $test['targetId']); if (isset($test['error'])) { $this->fail("$label - Unexpected success"); } } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { if (isset($test['error'])) { $this->assertSame( $test['error'], $e->getMessage(), "$label - Expected error message." ); } else { $this->fail("$label - Unexpected argument exception: ". $e->getMessage()); } } catch (Exception $e) { $this->fail( "$label - Unexpected exception (" . get_class($e) .') :'. $e->getMessage() ); } if (isset($test['expect'])) { $query = P4_File_Query::create()->addFilespec("$prefix/..."); $files = P4_File::fetchAll($query); $filespecs = array(); foreach ($files as $file) { if ($file->isDeleted()) { continue; } $filespecs[] = $file->getFilespec(); } $this->assertSame( $test['expect'], $filespecs, "$label - Expected category layout." ); } } } /** * Test that addEntry accepts integer entries. */ public function testAddIntegerEntry() { $friend = new P4Cms_Categorization_Friend; $friend->setId('test'); $friend->addEntry(1); $entries = $friend->getEntries(); $this->assertSame(array('1'), $entries, 'Expected entries'); } /** * Test addEntry with bogus entry. */ public function testAddBogusEntry() { $dir = new P4Cms_Categorization_Dir; $dir->setId('test'); try { $dir->addEntry(null); $this->fail('Unexpected success adding a null entry.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { $this->assertSame( "Cannot add entries; all entries must either be strings or known entry types.", $e->getMessage(), 'Expected error adding a null entry.' ); } catch (Exception $e) { $this->fail( 'Unexpected exception adding a null entry (' . get_class($e) .') :'. $e->getMessage() ); } try { $dir->addEntries(array(null, 'four')); $this->fail('Unexpected success adding a list with bogus entry.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { $this->assertSame( "Cannot add entries; all entries must either be strings or known entry types.", $e->getMessage(), 'Expected error adding a list with bogus entry.' ); } catch (Exception $e) { $this->fail( 'Unexpected exception adding a list with bogus entry (' . get_class($e) .') :'. $e->getMessage() ); } try { $dir->addEntries(array(array(null, null))); $this->fail('Unexpected success adding a list with bogus entry+sort value.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { $this->assertSame( "Cannot add entries; all entries must either be strings or known entry types.", $e->getMessage(), 'Expected error adding a list with bogus entry+sort value.' ); } catch (Exception $e) { $this->fail( 'Unexpected exception adding a list with bogus entry (' . get_class($e) .') :'. $e->getMessage() ); } } /** * Test deleteEntry with bogus entry. */ public function testDeleteBogusEntry() { $dir = new P4Cms_Categorization_Dir; $dir->setId('test'); try { $dir->deleteEntry(null); $this->fail('Unexpected success deleting a null entry.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { $this->assertSame( "Cannot delete entries; all entries must either be strings or known entry types.", $e->getMessage(), 'Expected error deleting a null entry.' ); } catch (Exception $e) { $this->fail( 'Unexpected exception deleting a null entry (' . get_class($e) .') :'. $e->getMessage() ); } try { $dir->deleteEntries(array(null, 'four')); $this->fail('Unexpected success deleting list with bogus entry.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { $this->assertSame( "Cannot delete entries; all entries must either be strings or known entry types.", $e->getMessage(), 'Expected error deleting list with bogus entry.' ); } catch (Exception $e) { $this->fail( 'Unexpected exception deleting list with bogus entry (' . get_class($e) .') :'. $e->getMessage() ); } } /** * Test recursive entry fetching. */ public function testGetEntriesRecursively() { $content = $this->_makeContent( array( 'test1' => 'One', 'test2' => 'Two', 'test3' => 'Three', 'test4' => 'Four', ) ); $dir = new P4Cms_Categorization_Dir; $dir->setId('deep') ->setTitle('Deep Category') ->setDescription('Deep Category Description') ->save(); $dir->addEntry('test1'); $dir2 = P4Cms_Categorization_Dir::store('deep/subdir1'); $this->assertSame('deep/subdir1', $dir2->getId(), 'Exected id for dir2'); $dir2->addEntry('test2'); $dir3 = P4Cms_Categorization_Dir::store('deep/subdir1/deeper'); $dir3->addEntry('test3'); $dir4 = P4Cms_Categorization_Dir::store('deep/subdir1/deeper/subdir2'); $dir4->addEntry('test4'); // start with the parent directory $entries = $dir->getEntries(); sort($entries); $this->assertSame( array('test1'), $entries, 'Expected non-recursive entries for dir.' ); $entries = $dir->getEntries(array('recursive' => true)); $this->assertSame( array('test4', 'test1', 'test3', 'test2'), $entries, 'Expected recursive entries for dir.' ); // again with subdir1 $entries = $dir2->getEntries(); sort($entries); $this->assertSame( array('test2'), $entries, 'Expected non-recursive entries for dir2.' ); $entries = $dir2->getEntries(array('recursive' => true)); $this->assertSame( array('test4', 'test3', 'test2'), $entries, 'Expected recursive entries for dir2.' ); // again, but fetch objects $entries = $dir2->getEntries(array('recursive' => true, 'dereference' => true)); $this->assertEquals(3, count($entries), "Expected object count."); $this->assertSame('test4', $entries['test4']->id, 'Expected object id #1'); $this->assertSame('test3', $entries['test3']->id, 'Expected object id #2'); $this->assertSame('test2', $entries['test2']->id, 'Expected object id #3'); } /** * Test encoding/decoding of entry ids. */ public function testEncodeDecodeEntryId() { $tests = array( array( 'label' => __LINE__ .': encode null', 'value' => null, 'method' => 'encodeEntryId', 'expect' => null, 'error' => 'Cannot encode entry id; id not set or has no length.', ), array( 'label' => __LINE__ .': encode empty string', 'value' => '', 'method' => 'encodeEntryId', 'expect' => null, 'error' => 'Cannot encode entry id; id not set or has no length.', ), array( 'label' => __LINE__ .': encode string', 'value' => 'the quick brown fox jumped over the lazy dog', 'method' => 'encodeEntryId', 'expect' =>'_74686520717569636b2062726f776e20666f78206a756d706564206f76657220746865206c617a7920646f67', 'error' => null, ), array( 'label' => __LINE__ .': decode null', 'value' => null, 'method' => 'decodeEntryId', 'expect' => null, 'error' => 'Cannot decode entry id; encoded id not set or has no length.', ), array( 'label' => __LINE__ .': decode empty string', 'value' => '', 'method' => 'decodeEntryId', 'expect' => null, 'error' => 'Cannot decode entry id; encoded id not set or has no length.', ), array( 'label' => __LINE__ .': decode string', 'value' => '_74686520717569636b2062726f776e20666f78206a756d706564206f76657220746865206c617a7920646f67', 'method' => 'decodeEntryId', 'expect' => 'the quick brown fox jumped over the lazy dog', 'error' => null, ), array( 'label' => __LINE__ .': decode bogus encoding', 'value' => '_#', 'method' => 'decodeEntryId', 'expect' => null, 'error' => 'Cannot decode entry id; encoded id contains invalid characters.', ), ); foreach ($tests as $test) { $label = $test['label']; $result = null; try { $result = P4Cms_Categorization_Dir::$test['method']($test['value']); if (isset($test['error'])) { $this->fail("$label - Unexpected success"); } } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { if (isset($test['error'])) { $this->assertSame( $test['error'], $e->getMessage(), "$label - expected error." ); } else { $this->fail("$label - Unexpected argument exception: ". $e->getMessage()); } } catch (Exception $e) { $this->fail( "$label - Unexpected exception (" . get_class($e) .') :'. $e->getMessage() ); } if (isset($test['expect'])) { $this->assertSame( $test['expect'], $result, "$label - expected result" ); } } // test roundtrip $testString = 'http://search.perforce.com/search?q=deleted%20files&site=kb!@#$%^&*()-_=+,<.>/?;:\'"[{]}\\'; $this->assertSame( $testString, P4Cms_Categorization_Dir::decodeEntryId(P4Cms_Categorization_Dir::encodeEntryId($testString)), 'Expected round trip to work.' ); } /** * Test entry handling. */ public function testEntryHandling() { // create several content entries. $content = $this->_makeContent( array( 'test1' => 'One', 'test2' => 'Two', 'test3' => 'Three', 'test4' => 'Four', 'test5' => 'Five', 'test6' => 'Six', ) ); $dir = new P4Cms_Categorization_Dir; $dir->setId('category') ->setTitle('Test Category') ->setDescription('Test Category Description') ->save(); $this->assertFalse($dir->hasEntries(), 'Expect to have no entries at start'); $this->assertEquals(0, count($dir->getEntries()), 'Expect entry count to be 0 at start'); // add an entry $dir->addEntry('test1'); $this->assertTrue($dir->hasEntries(), 'Expect to have entries after adding one'); $entries = $dir->getEntries(); $this->assertSame('test1', $entries[0], 'Expected entry'); // add a couple more entries $dir->addEntries(array('test2', 'test3')); $this->assertTrue($dir->hasEntries(), 'Expect to have entries after adding two and three'); $entries = $dir->getEntries(); $this->assertSame( array('test1', 'test3', 'test2'), $entries, 'Expected entries after add test2, test3' ); // add an object $dir->addEntry($content[3]); $entries = $dir->getEntries(); $this->assertSame( array('test4', 'test1', 'test3', 'test2'), $entries, 'Expected entries after add test4' ); // try adding an object that does not have a getId() method try { $object = new stdClass; $dir->addEntry($object); $this->fail('Unexpected success adding object without getId()'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { $this->assertSame( "Cannot add entries; all entries must either be strings or known entry types.", $e->getMessage(), 'Expected error adding object without getId()' ); } // add a couple more objects $dir->addEntries(array($content[4], $content[5])); $entries = $dir->getEntries(); $this->assertSame( array('test5', 'test4', 'test1', 'test6', 'test3', 'test2'), $entries, 'Expected entries after add test5, test6' ); // try to add entries with a non-array try { $dir->addEntries('not an array'); $this->fail('Unexpected success adding a non-list.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { $this->assertSame( 'Cannot add entries; you must provide an array of entries.', $e->getMessage(), 'Expected error adding a non-list.' ); } catch (Exception $e) { $this->fail( 'Unexpected exception adding a non-list (' . get_class($e) .') :'. $e->getMessage() ); } // delete an entry $dir->deleteEntry('test1'); $entries = $dir->getEntries(); $this->assertSame( array('test5', 'test4', 'test6', 'test3', 'test2'), $entries, 'Expected entries after delete one' ); // delete entries $dir->deleteEntries(array('test2', $content[4])); $entries = $dir->getEntries(); $this->assertSame( array('test4', 'test6', 'test3'), $entries, 'Expected entries after delete test2, test5' ); // try to delete a non-list try { $dir->deleteEntries('not a list'); $this->fail('Unexpected success deleting a non-list.'); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { $this->assertSame( 'Cannot delete entries; you must provide an array of entries.', $e->getMessage(), 'Expected error deleting a non-list.' ); } catch (Exception $e) { $this->fail( 'Unexpected exception deleting a non-list (' . get_class($e) .') :'. $e->getMessage() ); } } /** * Test various methods when category not setup. */ public function testCategoryWithoutSetup() { $dir = new P4Cms_Categorization_Dir; $tests = array( array( 'label' => __LINE__ .': getEntries', 'method' => 'getEntries', 'params' => array(), 'error' => 'Cannot get entries; category id is not set.', ), array( 'label' => __LINE__ .': getEntries (recursive)', 'method' => 'getEntries', 'params' => array('recursive' => true), 'error' => 'Cannot get entries; category id is not set.', ), array( 'label' => __LINE__ .': deleteEntries', 'method' => 'deleteEntries', 'params' => array('1', '2', '3'), 'error' => 'Cannot delete entries; category id is not set.', ), array( 'label' => __LINE__ .': addEntry', 'method' => 'addEntry', 'params' => 'entry', 'error' => 'Cannot add entries; category id is not set.', ), array( 'label' => __LINE__ .': hasChildren', 'method' => 'hasChildren', 'params' => null, 'error' => 'Cannot get children; category id is not set.', ), array( 'label' => __LINE__ .': delete', 'method' => 'delete', 'params' => null, 'error' => 'Cannot delete category; category id is not set.', ), ); foreach ($tests as $test) { $label = $test['label']; $method = $test['method']; try { $dir->$method($test['params']); $this->fail("$label - Unexpected success."); } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (P4Cms_Categorization_Exception $e) { $this->assertSame( $test['error'], $e->getMessage(), "$label - Expected error." ); } catch (Exception $e) { $this->fail( "$label - Unexpected exception (" . get_class($e) .') :'. $e->getMessage() ); } } } /** * Test getEntries with $dereference set. */ public function testGetEntriesMakeObjects() { $content = $this->_makeContent( array( 'test1' => 'One', ) ); // setup friend tests, where dereferenceEntry has not been implemented. $index = P4Cms_Categorization_Dir::CATEGORY_FILENAME; $file1 = new P4_File; $file1->setFilespec("//depot/friends/test/$index") ->add() ->setLocalContents('') ->submit("added friend test/$index"); $encodedId = P4Cms_Categorization_Dir::encodeEntryId('test1'); $file2 = new P4_File; $file2->setFilespec("//depot/friends/test/$encodedId") ->add() ->setLocalContents('') ->submit('added friend test/test1'); // verify that the friend class won't create objects via getEntries $friend = new P4Cms_Categorization_Friend; $friend->setId('test'); $entries = $friend->getEntries(array('dereference' => true)); $this->assertSame( array('test1'), $entries, "Expected getEntries() return list of entry ids even if tried to dereference." ); // setup dir tests, where dereference has been implemented. $file3 = new P4_File; $file3->setFilespec("//depot/folders/test/$index") ->add() ->setLocalContents('') ->submit("added dir test/$index"); $encodedId = P4Cms_Categorization_Dir::encodeEntryId('test1'); $file4 = new P4_File; $file4->setFilespec("//depot/folders/test/$encodedId") ->add() ->setLocalContents('') ->submit('added dir test/test1'); // verify that the dir class can create objects via getEntries $dir = new P4Cms_Categorization_Dir; $entries = $dir->setId('test')->getEntries(array('dereference' => true)); $this->assertEquals(1, count($entries), 'Expected entries count.'); $this->assertSame('test1', $entries['test1']->id, 'Expected object id'); } /** * Test dereferenceEntries. */ public function testDereferenceEntries() { // create entries $create = array( 'deref1' => 'One', 'deref2' => 'Two', 'deref3' => 'Three', ); $content = $this->_makeContent($create); // verify that the friend class won't dereference entries. $friend = new P4Cms_Categorization_Friend; $friend->setId('test') ->addEntries(array_keys($create)); $entries = $friend->getEntries(array('paths' => array(1, 2, 3))); $this->assertSame( array_keys($create), $entries, "Expected empty entries list." ); // verify that the dir class can dereference entries. $dir = new P4Cms_Categorization_Dir; $dir->setId('test') ->addEntries(array_keys($create)); $entries = $dir->getEntries( array( 'dereference' => true, 'paths' => array_keys($create) ) ); $titles = array(); foreach ($entries as $entry) { $titles[] = $entry->getValue('title'); } // assume entries are sorted by title by default $expected = array('One', 'Three', 'Two'); $this->assertEquals($expected, $titles, 'Expected objects'); } /** * Test adding an entry that already exists, and deleting an entry that does not exist. */ public function testAddDupeAndDeleteUnknown() { $content = $this->_makeContent( array( 'test1' => 'One', ) ); $dir = new P4Cms_Categorization_Dir; $dir->setId('category') ->setTitle('Test Category') ->setDescription('Test Category Description') ->save(); $this->assertFalse($dir->hasEntries(), 'Expect to have no entries at start.'); $this->assertEquals(0, count($dir->getEntries()), 'Expect entry count to be 0 at start.'); // try deleting an entry $dir->deleteEntry('test1'); // add an entry $dir->addEntry('test1'); $this->assertTrue($dir->hasEntries(), 'Expect an entry'); $this->assertEquals(1, count($dir->getEntries()), 'Expect entry count to be 1 after adding entry.'); // try adding same entry again $dir->addEntry('test1'); $this->assertEquals(1, count($dir->getEntries()), 'Expect entry count to be 1 after adding dupe.'); // delete the entry $dir->deleteEntry('test1'); $this->assertFalse($dir->hasEntries(), 'Expect to have no entries at end.'); // add the entry again to make sure files with delete status have no influence. $dir->addEntry('test1'); $this->assertEquals(1, count($dir->getEntries()), 'Expect entry count to be 1 after adding dupe.'); } /** * Test setEntryCategories with bad parameters */ public function testSetEntryCategoriesBadParams() { $tests = array( array( 'label' => __LINE__ .': null entry', 'entry' => null, 'categories' => null, 'error' => 'Cannot set categories; the entry must either be a string or known data structure.', ), array( 'label' => __LINE__ .': empty entry', 'entry' => '', 'categories' => null, 'error' => 'Cannot set categories; the entry must either be a string or known data structure.', ), array( 'label' => __LINE__ .': string entry, null categories', 'entry' => 'string', 'categories' => null, 'error' => 'Cannot set categories; categories must be an array.', ), ); foreach ($tests as $test) { $label = $test['label']; try { P4Cms_Categorization_Dir::setEntryCategories($test['entry'], 'A', $test['categories']); if (isset($test['error'])) { $this->fail("$label - unexpected success"); } } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { if (isset($test['error'])) { $this->assertSame( $test['error'], $e->getMessage(), "$label - Expected error" ); } else { $this->fail("$label - unexpected argument exception: ". $e->getMessage()); } } catch (Exception $e) { $this->fail("$label - Unexpected exception (" . get_class($e) .') :'. $e->getMessage()); } } } /** * Test setEntryCategories behaviour. */ public function testSetEntryCategories() { $tests = array( array( 'label' => __LINE__ .': foo, set no cats, with no cats (noop)', 'entry' => 'foo', 'setCats' => array(), 'expectedCats' => array(), 'expectedSet' => array(), ), array( 'label' => __LINE__ .': foo, set cats, with no cats (adds/creates)', 'entry' => 'foo', 'setCats' => array('one', 'one/two'), 'expectedCats' => array('one', 'one/two'), 'expectedSet' => array('one', 'one/two'), ), array( 'label' => __LINE__ .': bar, set no cats, with some cats (noop)', 'entry' => 'bar', 'setCats' => array(), 'expectedCats' => array('one', 'one/two'), 'expectedSet' => array(), ), array( 'label' => __LINE__ .': bar, set some cats, with some cats (adds/creates)', 'entry' => 'bar', 'setCats' => array('one', 'three'), 'expectedCats' => array('one', 'one/two', 'three'), 'expectedSet' => array('one', 'three'), ), array( 'label' => __LINE__ .': bar, set same cats, with some cats (noop)', 'entry' => 'bar', 'setCats' => array('one', 'three'), 'expectedCats' => array('one', 'one/two', 'three'), 'expectedSet' => array('one', 'three'), ), array( 'label' => __LINE__ .': baz, set some cats, with some cats (adds/creates)', 'entry' => 'baz', 'setCats' => array('one', 'four'), 'expectedCats' => array('four', 'one', 'one/two', 'three'), 'expectedSet' => array('four', 'one'), ), array( 'label' => __LINE__ .': baz, set some cats, with some cats (adds/deletes)', 'entry' => 'baz', 'setCats' => array('three', 'four'), 'expectedCats' => array('four', 'one', 'one/two', 'three'), 'expectedSet' => array('four', 'three'), ), array( 'label' => __LINE__ .': baz, set no cats, with some cats (deletes)', 'entry' => 'baz', 'setCats' => array(), 'expectedCats' => array('four', 'one', 'one/two', 'three'), 'expectedSet' => array(), ), ); foreach ($tests as $test) { // create categories. foreach ($test['setCats'] as $id) { $category = new P4Cms_Categorization_Dir; $category->setId($id)->save(); } $label = $test['label']; P4Cms_Categorization_Dir::setEntryCategories($test['entry'], $test['setCats']); // check that categories exist as expected $categories = P4Cms_Categorization_Dir::fetchAll(); $ids = array(); foreach ($categories as $cat) { $ids[] = $cat->getId(); } $this->assertSame($test['expectedCats'], $ids, "$label - Expected category layout"); // check that the categories associated are as expected $categories = P4Cms_Categorization_Dir::fetchAllByEntry($test['entry']); $ids = array(); foreach ($categories as $cat) { $ids[] = $cat->getId(); } $this->assertSame($test['expectedSet'], $ids, "$label - Expected category associations"); } } /** * Test fetchAllByEntry with bad parameters. */ public function testFetchAllByEntryBadParams() { $tests = array( array( 'label' => __LINE__ .': null', 'entry' => null, 'error' => 'Cannot get categories; the entry must either be a string or known entry type.', ), array( 'label' => __LINE__ .': empty string', 'entry' => '', 'error' => 'Cannot get categories; the entry must either be a string or known entry type.', ), array( 'label' => __LINE__ .': string', 'entry' => 'string', 'error' => null, 'expected' => array(), ), ); foreach ($tests as $test) { $label = $test['label']; try { $categories = P4Cms_Categorization_Dir::fetchAllByEntry($test['entry']); if (isset($test['error'])) { $this->fail("$label - unexpected success"); } } catch (PHPUnit_Framework_AssertionFailedError $e) { $this->fail($e->getMessage()); } catch (InvalidArgumentException $e) { if (isset($test['error'])) { $this->assertSame( $test['error'], $e->getMessage(), "$label - Expected error" ); } else { $this->fail("$label - unexpected argument exception: ". $e->getMessage()); } } catch (Exception $e) { $this->fail("$label - Unexpected exception (" . get_class($e) .') :'. $e->getMessage()); } if (!isset($test['error'])) { $ids = array(); foreach ($categories as $category) { $ids[] = $category->getId(); } $this->assertSame( $test['expected'], $ids, "$label - expected category ids" ); } } } /** * Test fetchAllByEntry behaviour. */ public function testFetchAllByEntry() { // attempt to get categories when none are defined. $categories = P4Cms_Categorization_Dir::fetchAllByEntry('test'); $ids = array(); foreach ($categories as $category) { $ids[] = $category->getId(); } $this->assertEquals(array(), $ids, 'Expect no categories when none defined'); // make some categories, and try again $one = P4Cms_Categorization_Dir::store(array('id' => 'one', 'title' => 'One')); $two = P4Cms_Categorization_Dir::store(array('id' => 'one/two', 'title' => 'Two in One')); $categories = P4Cms_Categorization_Dir::fetchAllByEntry('test'); $ids = array(); foreach ($categories as $category) { $ids[] = $category->getId(); } $this->assertEquals(array(), $ids, 'Expect no categories when some defined, but not populated'); // add some entries, one for the target $one->addEntry('entry1'); $two->addEntry('entry2'); $two->addEntry('test'); $categories = P4Cms_Categorization_Dir::fetchAllByEntry('test'); $ids = array(); foreach ($categories as $category) { $ids[] = $category->getId(); } $this->assertEquals(array('one/two'), $ids, 'Expect one category after initial entry creation'); // add another target entry. $one->addEntry('test'); $categories = P4Cms_Categorization_Dir::fetchAllByEntry('test'); $ids = array(); foreach ($categories as $category) { $ids[] = $category->getId(); } $this->assertEquals( array($one->getId(), $two->getId()), $ids, 'Expect two category after second entry creation' ); } /** * Test getDepth */ public function testGetDepth() { $depths = range(0, 10); $category = new P4Cms_Categorization_Dir; foreach ($depths as $depth) { $id = 'foo' . str_repeat('/foo', $depth); $this->assertSame( $depth, $category->setId($id)->getDepth(), "Expected category depth of $depth." ); } } /** * Test get ancestors */ public function testGetAncestors() { // make a category tree. P4Cms_Categorization_Dir::store(array("id" => "foo")); P4Cms_Categorization_Dir::store(array("id" => "foo/bar")); P4Cms_Categorization_Dir::store(array("id" => "foo/bar/baz")); P4Cms_Categorization_Dir::store(array("id" => "foo/bar/baz/bof")); // get leaf and grab ancestry. $category = P4Cms_Categorization_Dir::fetch("foo/bar/baz/bof"); $ancestors = $category->getAncestors(); // ensure 3 ancestors. $this->assertSame(3, $ancestors->count()); // ensure expected ids. $expect = array('foo', 'foo/bar', 'foo/bar/baz'); $actual = $ancestors->invoke('getId'); $this->assertSame($expect, $actual, 'Expected ids from getAncestors'); // test just the ids $ids = $category->getAncestorIds(); $this->assertSame($expect, $ids, 'Expected ids from getAncestorIds'); $dir = new P4Cms_Categorization_Dir; $this->assertSame( array(), $dir->setId('one')->getAncestorIds(), 'Expected ids when category has no ancestor' ); } /** * Test simple accessor/mutator methods. */ public function testAccessorsMutators() { $dir = new P4Cms_Categorization_Dir; $dir->setId('one/two') ->setTitle('title') ->setDescription('description'); $this->assertEquals('one/two', $dir->getId(), 'expected id'); $this->assertEquals('two', $dir->getBaseId(), 'expected base id'); $this->assertEquals('title', $dir->getTitle(), 'expected title'); $this->assertEquals('description', $dir->getDescription(), 'expected description'); } /** * Test helper method to create specified content records. * * @param array $entries An array of id => title for the content records to create. * @return array An array of the created content entries. */ protected function _makeContent(array $entries) { $type = new P4Cms_Content_Type(); $type->setId("basic-page") ->setLabel("Basic Page") ->setElements( array( "title" => array( "type" => "text", "options" => array("label" => "Title", "required" => true) ), "description" => array( "type" => "text", "options" => array("label" => "Description") ) ) ) ->save(); $created = array(); foreach ($entries as $id => $title) { $entry = new P4Cms_Content; $entry->setId($id) ->setValue('contentType', 'basic-page') ->setValue('title', $title) ->save(); $created[] = $entry; } return $created; } }