Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/site/app/tests/models/memcaching_model.test.php
diff options
context:
space:
mode:
Diffstat (limited to 'site/app/tests/models/memcaching_model.test.php')
-rw-r--r--site/app/tests/models/memcaching_model.test.php289
1 files changed, 289 insertions, 0 deletions
diff --git a/site/app/tests/models/memcaching_model.test.php b/site/app/tests/models/memcaching_model.test.php
new file mode 100644
index 0000000..f2035c3
--- /dev/null
+++ b/site/app/tests/models/memcaching_model.test.php
@@ -0,0 +1,289 @@
+<?php /* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is addons.mozilla.org site.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Frederic Wenzel <fwenzel@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/**
+ * This tests if the Model interface to Memcached works as expected.
+ */
+
+class MemcachingModelTest extends UnitTestCase {
+ var $Cache; // holds the cache object
+ var $testdata = array('User' => array(
+ 'username' => 'johndoe',
+ 'firstname' => 'John',
+ 'lastname' => 'Doe',
+ 'email' => 'johndoe@example.com'));
+ // for object caching: identification array, arbitrary
+ var $identifier = array('my_test_data', 1234, array('some', 'more', 'info'));
+
+ /**
+ * Constructor
+ */
+ function MemcachingModelTest() {
+ loadModel('Memcaching');
+ }
+
+ /**
+ * On load, load the memcache object, if the config says it should work.
+ * Also set up an empty mem cache for each test.
+ */
+ function setUp() {
+ if (!QUERY_CACHE) {
+ $this->pass('Memcached caching is not activated.');
+ return;
+ }
+
+ if (!$this->Cache) {
+ // Memcache extension exists
+ $this->assertTrue(class_exists('Memcache'), 'Memcache extension is installed');
+
+ // Memcaching model is instanciable and connects to server
+ $this->Cache = new Memcaching();
+ $this->assertTrue($this->Cache->memcacheConnected, 'Memcaching model is instanciable and connects to server');
+ }
+ $this->Cache->flush();
+ }
+
+ function testSet() {
+ if (!QUERY_CACHE) return;
+ $this->assertTrue($this->Cache->set('testdata', $this->testdata), 'storing something in the memcache');
+ }
+
+ function testGet() {
+ if (!QUERY_CACHE) return;
+ $this->Cache->set('testdata', $this->testdata);
+ $this->assertEqual($this->Cache->get('testdata'), $this->testdata, 'retrieving something from the memcache');
+ }
+
+ function testAdd() {
+ if (!QUERY_CACHE) return;
+ $this->assertTrue($this->Cache->add('testdata', $this->testdata), 'add() something new to the memcache');
+ $this->assertFalse($this->Cache->add('testdata', array()), 'add() does not overwrite an existing key in the memcache');
+ }
+
+ function testReplace() {
+ if (!QUERY_CACHE) return;
+ $this->Cache->set('testdata', $this->testdata);
+ $this->assertTrue($this->Cache->replace('testdata', array('something', 'else')), 'replace() replaces existing data in the memcache');
+ $this->assertFalse($this->Cache->replace('doesntexist', $this->testdata), 'replace() does not add a key that did not exist before');
+ }
+
+ function testDelete() {
+ if (!QUERY_CACHE) return;
+ $this->Cache->set('testdata', $this->testdata);
+ $this->assertTrue($this->Cache->delete('testdata'), 'deleting existing data out of the memcache succeeds');
+ $this->assertFalse($this->Cache->get('testdata'), 'deleting actually works');
+ $this->assertFalse($this->Cache->delete('cantdeletethis'), 'deleting nonexistant data returns false');
+ }
+
+ function testFlush() {
+ if (!QUERY_CACHE) return;
+ $this->Cache->set('testdata', $this->testdata);
+ $this->assertTrue($this->Cache->flush(), 'flushing the cache succeeds');
+ $this->assertFalse($this->Cache->get('testdata'), 'flushing the cache actually works');
+ }
+
+ /**
+ * This test was added to make sure empty result sets are stored just
+ * like every other piece of data.
+ */
+ function testStoreAndRetrieveEmpty() {
+ if (!QUERY_CACHE) return;
+ // storing an empty array in the memcache...
+ $this->assertTrue($this->Cache->set('emptydata', array()), 'storing an empty array succeeds');
+
+ // ... and retrieving it
+ $_retrieved = $this->Cache->get('emptydata');
+ $this->assertTrue(is_array($_retrieved) && empty($_retrieved), 'retrieving an empty array from the memcache works');
+ }
+
+ /**
+ * Here we want to verify that when someone calls findAll() or query()
+ * they still get unique results by $model->name.
+ */
+ function testUniqueRecordsByModelName() {
+ if (!QUERY_CACHE) return;
+ loadmodel('Addontype');
+ loadmodel('Platform');
+
+ // Load test models.
+ $model1 =& new Addontype();
+ $model2 =& new Platform();
+
+ // Do one callback to load the cache.
+ $model1->findAll(null, null, null, null, null, -1);
+ $model2->findAll(null, null, null, null, null, -1);
+
+ // Perform a second callback to see what the cache spits out the serialize.
+ $addontypes = serialize($model1->findAll(null, null, null, null, null, -1));
+ $platforms = serialize($model2->findAll(null, null, null, null, null, -1));
+
+ $this->assertNotEqual($addontypes, $platforms, 'Addontypes and Platforms differed by model name when pulled from cache by findAll().');
+ }
+
+ /**
+ * Test that we can get extended stats.
+ */
+ function testGetExtendedStats() {
+ if (!QUERY_CACHE) return;
+ $this->assertIsA($this->Cache->getExtendedStats(),'array');
+ }
+
+ /**
+ * Check if memcache-ing does not store query errors
+ */
+ function testMemcacheNoErrors() {
+ if (!QUERY_CACHE) return;
+
+ loadmodel('Addon');
+ $this->Addon = new Addon();
+
+ // replace Memcaching object by mock object to catch unwanted calls
+ Mock::generate('MyMemcaching');
+ $this->Addon->Cache = new MockMyMemcaching();
+ // assert that the model does not try to store the invalid result to memcache
+ $this->Addon->Cache->expectNever('set', 'invalid query should not be cached');
+
+ $invalidquery = 'SELECT something invalid'; // SELECT query that'll fail
+
+ // execute invalid query
+ @$this->Addon->query($invalidquery);
+ }
+
+ /* * * Tests for object caching * * */
+
+ /**
+ * Test the generation of a cache key
+ */
+ function testGenerateCacheKey() {
+ if (!QUERY_CACHE) return;
+
+ // make a second, different identifier array
+ $ident2 = $this->identifier;
+ array_pop($ident2);
+ $ident2[] = 'something else';
+
+ $key1 = $this->Cache->_generateCacheKey($this->identifier);
+ $key2 = $this->Cache->_generateCacheKey($ident2);
+ $this->assertFalse(empty($key1) || empty($key2), '_generateCacheKey() generating nonempty cache keys');
+
+ $this->assertNotEqual($key1, $key2, 'different cache keys for different identifiers');
+ }
+
+ /**
+ * Try to write, then read an object through the object caching interface,
+ * including writing to an expiration list.
+ */
+ function testReadWriteCacheObject() {
+ if (!QUERY_CACHE) return;
+
+ Mock::generatePartial('Memcaching', 'Mock_testRWCacheObj', array('set', 'get',
+ '_generateCacheKey', '_addObjectToExpirationList'));
+ $mockCache =& new Mock_testRWCacheObj();
+
+ // mock a known cache key
+ $_mock_cachekey = 'amo_unittest_cachekey';
+ $mockCache->setReturnValue('_generateCacheKey', $_mock_cachekey, array($this->identifier));
+
+ // assertions for writing
+ $mockCache->setReturnValue('set', true);
+ $mockCache->expectOnce('set', array($_mock_cachekey, $this->testdata, '*', '*'),
+ 'storing object in cache');
+
+ // assertions for reading
+ $mockCache->expectOnce('get', array($_mock_cachekey), 'reading object back from cache');
+
+ // assertions for expiration lists
+ $_expiration_lists = array('addon' => array(1, 2));
+ $startlist = array('amo_unittest_existing_cachekey');
+ $mockCache->expectCallCount('_addObjectToExpirationList', 2);
+
+ // write an object to the cache
+ $mockCache->writeCacheObject($this->identifier, $this->testdata, $_expiration_lists);
+
+ // read the same object back
+ $mockCache->readCacheObject($this->identifier);
+
+
+ // count calls
+ $mockCache->tally();
+ }
+
+ /**
+ * Test marking/flushing of expiration lists
+ */
+ function testExpirationListFlush() {
+ if (!QUERY_CACHE) return;
+
+ // generate empty flush list
+ global $flush_lists;
+ $flush_lists = array();
+
+ Mock::generatePartial('Memcaching', 'Mock_testExpListFlush', array('get', 'delete'));
+ $this->Cache =& new Mock_testExpListFlush();
+
+ // add list ids to flush list
+ $this->Cache->markListForFlush('list 1');
+ $this->Cache->markListForFlush('list 2');
+ $this->assertEqual(count($flush_lists), 2, 'adding items to cache flush list');
+
+ // prepare a list, then have it flushed from memcache
+ $flush_lists = array('list1', 'list2');
+ $testlist1 = array('foo1', 'bar1');
+ $testlist2 = array('foo2', 'bar2');
+ $this->Cache->setReturnValue('get', $testlist1, array('list1'));
+ $this->Cache->setReturnValue('get', $testlist2, array('list2'));
+ // assert that the lists are going to be read
+ $this->Cache->expectCallCount('get', 2);
+ // ... and emptied (6 times: 2 times 2 objects in the lists, plus the lists themselves)
+ $this->Cache->expectCallCount('delete', 6);
+
+ $this->Cache->flushMarkedLists();
+
+ $this->Cache->tally();
+ }
+
+}
+
+/**
+ * Mock memcache object
+ */
+class MyMemcaching {
+ function get() {}
+ function set() {}
+}
+
+?>