]> gitweb.fluxo.info Git - lorea/elgg.git/commitdiff
Refs #1200: Changed logic in elgg_get_entity_type_subtype_where_sql() to return FALSE...
authorbrettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544>
Thu, 4 Feb 2010 04:36:34 +0000 (04:36 +0000)
committerbrettp <brettp@36083f99-b078-4883-b0ff-0f9b5a30f544>
Thu, 4 Feb 2010 04:36:34 +0000 (04:36 +0000)
git-svn-id: http://code.elgg.org/elgg/trunk@3901 36083f99-b078-4883-b0ff-0f9b5a30f544

engine/lib/elgglib.php
engine/lib/entities.php
engine/tests/api/entity_getter_functions.php [new file with mode: 0644]

index ce383ed8c6d4e076f5d9135568a289a5621a88f6..357141baa1192cbe2513e2a5d5434c21fa4e50b8 100644 (file)
@@ -2686,6 +2686,15 @@ function elgg_boot() {
        elgg_view_register_simplecache('js/initialise_elgg');
 }
 
+/**
+ * Runs unit tests for the API.
+ */
+function elgg_api_test($hook, $type, $value, $params) {
+       global $CONFIG;
+       $value[] = $CONFIG->path . 'engine/tests/api/entity_getter_functions.php';
+       return $value;
+}
+
 /**
  * Some useful constant definitions
  */
@@ -2697,3 +2706,4 @@ define('ACCESS_FRIENDS',-2);
 
 register_elgg_event_handler('init','system','elgg_init');
 register_elgg_event_handler('boot','system','elgg_boot',1000);
+register_plugin_hook('unit_test', 'system', 'elgg_api_test');
\ No newline at end of file
index 7bc1d4cdadc25adcec468cae67fd4a19f1b2189d..930772943be6644e383dde7559ed58e9016a04a8 100644 (file)
@@ -1954,67 +1954,106 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
                        $subtypes = array($subtypes);
                }
 
-               // subtypes are based upon types, so we need to look at each
-               // type individually to get the right subtype id.
+               // decrementer for valid types.  Return FALSE if no valid types
+               $valid_types_count = count($types);
+               $valid_subtypes_count = 0;
+               // remove invalid types to get an accurate count of
+               // valid types for the invalid subtype detection to use
+               // below.
+               // also grab the count of ALL subtypes on valid types to decrement later on
+               // and check against.
+               //
+               // yes this is duplicating a foreach on $types.
                foreach ($types as $type) {
                        if (!in_array($type, $valid_types)) {
-                               return FALSE;
+                               $valid_types_count--;
+                               unset ($types[array_search($type, $types)]);
+                       } else {
+                               // do the checking (and decrementing) in the subtype section.
+                               $valid_subtypes_count += count($subtypes);
                        }
+               }
 
+               // return false if nothing is valid.
+               if (!$valid_types_count) {
+                       return FALSE;
+               }
+
+               // subtypes are based upon types, so we need to look at each
+               // type individually to get the right subtype id.
+               foreach ($types as $type) {
                        $subtype_ids = array();
                        if ($subtypes) {
-                               // if there is only one subtype and it is is not 0 and it invalid return false.
-                               // if the type is 0 or null, let it through.
-                               // if the type is set but the subtype is FALSE, return false.
-                               if (count($subtypes) === 1) {
-                                       if ($subtypes[0] && !get_subtype_id($type, $subtypes[0])) {
-                                               return FALSE;
-                                       }
-                               }
-
                                // subtypes can be NULL or '' or 0, which means "no subtype"
                                foreach ($subtypes as $subtype) {
                                        // if a subtype is sent that doesn't exist
                                        if (0 === $subtype || $subtype_id = get_subtype_id($type, $subtype)) {
                                                $subtype_ids[] = (0 === $subtype) ? 0 : $subtype_id;
                                        } else {
-                                               // @todo should return false.
-                                               //return FALSE;
-
+                                               $valid_subtypes_count--;
                                                elgg_log("Type-subtype $type:$subtype' does not exist!", 'WARNING');
+                                               // return false if we're all invalid subtypes in the only valid type
                                                continue;
                                        }
                                }
+
+                               if ($valid_subtypes_count <= 0) {
+                                       return FALSE;
+                               }
                        }
-               }
-               //if ($subtype_ids_str = implode(',', $subtype_ids)) {
-               if (is_array($subtype_ids) && count($subtype_ids)) {
-                       $subtype_ids_str = implode(',', $subtype_ids);
-                       $wheres[] = "({$table}.type = '$type' AND {$table}.subtype IN ($subtype_ids_str))";
-               } else {
-                       $wheres[] = "({$table}.type = '$type')";
-               }
 
+                       if (is_array($subtype_ids) && count($subtype_ids)) {
+                               $subtype_ids_str = implode(',', $subtype_ids);
+                               $wheres[] = "({$table}.type = '$type' AND {$table}.subtype IN ($subtype_ids_str))";
+                       } else {
+                               $wheres[] = "({$table}.type = '$type')";
+                       }
+               }
        } else {
                // using type/subtype pairs
+               $valid_pairs_count = count($pairs);
+               $valid_pairs_subtypes_count = 0;
+
+               // same deal as above--we need to know how many valid types
+               // and subtypes we have before hitting the subtype section.
+               // also normalize the subtypes into arrays here.
                foreach ($pairs as $paired_type => $paired_subtypes) {
-                       if ($paired_subtypes && !is_array($paired_subtypes)) {
-                               $paired_subtypes = array($paired_subtypes);
+                       if (!in_array($paired_type, $valid_types)) {
+                               $valid_pairs_count--;
+                               unset ($pairs[array_search($paired_type, $pairs)]);
+                       } else {
+                               if ($paired_subtypes && !is_array($paired_subtypes)) {
+                                       $pairs[$paired_type] = array($paired_subtypes);
+                               }
+                               $valid_pairs_subtypes_count += count($paired_subtypes);
                        }
+               }
 
-                       if (is_array($paired_subtypes) && count($paired_subtypes)) {
+               if ($valid_pairs_count <= 0) {
+                       return FALSE;
+               }
+               foreach ($pairs as $paired_type => $paired_subtypes) {
+                       // this will always be an array because of line 2027, right?
+                       // no...some overly clever person can say pair => array('object' => null)
+                       if (is_array($paired_subtypes)) {
                                $paired_subtype_ids = array();
                                foreach ($paired_subtypes as $paired_subtype) {
                                        if ($paired_subtype && ($paired_subtype_id = get_subtype_id($paired_type, $paired_subtype))) {
                                                $paired_subtype_ids[] = $paired_subtype_id;
                                        } else {
-                                               // @todo should return false.
-                                               //return FALSE;
-                                               elgg_log("Paired type-subtype $paired_type:$paired_subtype' does not exist!", 'WARNING');
+                                               $valid_pairs_subtypes_count--;
+                                               elgg_log("Type-subtype $paired_type:$paired_subtype' does not exist!", 'WARNING');
+                                               // return false if we're all invalid subtypes in the only valid type
                                                continue;
                                        }
                                }
 
+                               // return false if there are no valid subtypes.
+                               if ($valid_pairs_subtypes_count <= 0) {
+                                       return FALSE;
+                               }
+
+
                                if ($paired_subtype_ids_str = implode(',', $paired_subtype_ids)) {
                                        $wheres[] = "({$table}.type = '$paired_type' AND {$table}.subtype IN ($paired_subtype_ids_str))";
                                }
@@ -2034,6 +2073,7 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
 }
 
 
+
 /**
  * Returns SQL for owner and containers.
  *
diff --git a/engine/tests/api/entity_getter_functions.php b/engine/tests/api/entity_getter_functions.php
new file mode 100644 (file)
index 0000000..2324374
--- /dev/null
@@ -0,0 +1,707 @@
+<?php
+
+/**
+ * Elgg Test Entity Getter Functions
+ * @package Elgg
+ * @subpackage Test
+ * @author Curverider Ltd
+ * @link http://elgg.org/
+ */
+class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
+       /**
+        * Called before each test object.
+        */
+       public function __construct() {
+               parent::__construct();
+       }
+
+       /**
+        * Called after each test method.
+        */
+       public function setUp() {
+               elgg_set_ignore_access(TRUE);
+               $this->entities = array();
+               $this->subtypes = array(
+                       'object' => array(),
+                       'user' => array(),
+                       'group' => array(),
+               //'site'        => array()
+               );
+
+               // sites are a bit wonky.  Don't use them just now.
+               $this->types = array('object', 'user', 'group');
+
+               // create some fun objects to play with.
+               // one with no subtype
+//             $e = new ElggObject();
+//             $e->subtype = $subtype;
+//             $e->save();
+
+               // and 5 with random subtypes
+               for ($i=0; $i<5; $i++) {
+                       $subtype = "test_object_subtype_" . rand();
+                       $e = new ElggObject();
+                       $e->subtype = $subtype;
+                       $e->save();
+                       $this->entities[] = $e;
+                       $this->subtypes['object'][] = $subtype;
+               }
+
+               // and users
+               for ($i=0; $i<5; $i++) {
+                       $subtype = "test_user_subtype_" . rand();
+                       $e = new ElggUser();
+                       $e->username = "test_user_" . rand();
+                       $e->subtype = $subtype;
+                       $e->save();
+                       $this->entities[] = $e;
+                       $this->subtypes['user'][] = $subtype;
+               }
+
+               // and groups
+               for ($i=0; $i<5; $i++) {
+                       $subtype = "test_group_subtype_" . rand();
+                       $e = new ElggGroup();
+                       $e->subtype = $subtype;
+                       $e->save();
+                       $this->entities[] = $e;
+                       $this->subtypes['group'][] = $subtype;
+               }
+       }
+
+       /**
+        * Called after each test method.
+        */
+       public function tearDown() {
+               //$this->swallowErrors();
+
+               foreach ($this->entities as $e) {
+                       $e->delete();
+                       unset($this->entities);
+               }
+       }
+
+       /**
+        * Called after each test object.
+        */
+       public function __destruct() {
+               parent::__destruct();
+       }
+
+
+       /*************************************************
+        * Helpers for getting random types and subtypes *
+        *************************************************/
+
+       /**
+        * Get a random valid subtype
+        *
+        * @param int $num
+        * @return array
+        */
+       public function getRandomValidTypes($num = 1) {
+               $r = array();
+
+               for ($i=1; $i<=$num; $i++) {
+                       do {
+                               $t = $this->types[array_rand($this->types)];
+                       } while (in_array($t, $r) && count($r) < count($this->types));
+
+                       $r[] = $t;
+               }
+
+               shuffle($r);
+               return $r;
+       }
+
+
+       /**
+        * Get a random valid subtype (that we just created)
+        *
+        * @param array $type Type of objects to return valid subtypes for.
+        * @param int $num of subtypes.
+        *
+        * @return array
+        */
+       public function getRandomValidSubtypes(array $types, $num = 1) {
+               $r = array();
+
+               for ($i=1; $i<=$num; $i++) {
+                       do {
+                               // make sure at least one subtype of each type is returned.
+                               if ($i-1 < count($types)) {
+                                       $type = $types[$i-1];
+                               } else {
+                                       $type = $types[array_rand($types)];
+                               }
+
+                               $k = array_rand($this->subtypes[$type]);
+                               $t = $this->subtypes[$type][$k];
+                       } while (in_array($t, $r));
+
+                       $r[] = $t;
+               }
+
+               shuffle($r);
+               return $r;
+       }
+
+       /**
+        * Return an array of invalid strings for type or subtypes.
+        *
+        * @param int $num
+        * @return arr
+        */
+       public function getRandomInvalids($num = 1) {
+               $r = array();
+
+               for ($i=1; $i<=$num; $i++) {
+                       $r[] = 'random_invalid_' . rand();
+               }
+
+               return $r;
+       }
+
+       /**
+        *
+        * @param unknown_type $num
+        * @return unknown_type
+        */
+       public function getRandomMixedTypes($num = 2) {
+               $have_valid = $have_invalid = false;
+               $r = array();
+
+               // need at least one of each type.
+               $valid_n = rand(1, $num-1);
+               $r = array_merge($r, $this->getRandomValidTypes($valid_n));
+               $r = array_merge($r, $this->getRandomInvalids($num - $valid_n));
+
+               shuffle($r);
+               return $r;
+       }
+
+       /**
+        * Get random mix of valid and invalid subtypes for types given.
+        *
+        * @param array $types
+        * @param unknown_type $num
+        * @return unknown_type
+        */
+       public function getRandomMixedSubtypes(array $types, $num = 2) {
+               $types_c = count($types);
+               $r = array();
+
+               // this can be more efficient but I'm very sleepy...
+
+               // want at least one of valid and invalid of each type sent.
+               for ($i=0; $i < $types_c && $num > 0; $i++) {
+                       // make sure we have a valid and invalid for each type
+                       if (true) {
+                               $type = $types[$i];
+                               $r = array_merge($r, $this->getRandomValidSubtypes(array($type), 1));
+                               $r = array_merge($r, $this->getRandomInvalids(1));
+
+                               $num -= 2;
+                       }
+               }
+
+               if ($num > 0) {
+                       $valid_n = rand(1, $num);
+                       $r = array_merge($r, $this->getRandomValidSubtypes($types, $valid_n));
+                       $r = array_merge($r, $this->getRandomInvalids($num - $valid_n));
+               }
+
+               //shuffle($r);
+               return $r;
+       }
+
+
+       /***********************************
+        * TYPE TESTS
+        ***********************************
+        * check for getting a valid type in all ways we can.
+        * note that these aren't wonderful tests as there will be
+        * existing entities so we can't test against the ones we just created.
+        * So these just test that some are returned and match the type(s) requested.
+        * It could definitely be the case that the first 10 entities retrieved are all
+        * objects.  Maybe best to limit to 4 and group by type.
+        */
+       public function testElggApiGettersValidTypeUsingType() {
+               $type_arr = $this->getRandomValidTypes();
+               $type = $type_arr[0];
+               $options = array(
+                       'type' => $type,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIsA($es, 'array');
+
+                       // should only ever return one object because of group by
+                       $this->assertIdentical(count($es), 1);
+                       foreach ($es as $e) {
+                               $this->assertTrue(in_array($e->getType(), $type_arr));
+                       }
+       }
+
+       public function testElggApiGettersValidTypeUsingTypesAsString() {
+               $type_arr = $this->getRandomValidTypes();
+               $type = $type_arr[0];
+               $options = array(
+                       'types' => $type,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIsA($es, 'array');
+
+                       // should only ever return one object because of group by
+                       $this->assertIdentical(count($es), 1);
+                       foreach ($es as $e) {
+                               $this->assertTrue(in_array($e->getType(), $type_arr));
+                       }
+       }
+
+       public function testElggApiGettersValidTypeUsingTypesAsArray() {
+               $type_arr = $this->getRandomValidTypes();
+               $type = $type_arr[0];
+               $options = array(
+                       'types' => $type_arr,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIsA($es, 'array');
+
+                       // should only ever return one object because of group by
+                       $this->assertIdentical(count($es), 1);
+                       foreach ($es as $e) {
+                               $this->assertTrue(in_array($e->getType(), $type_arr));
+                       }
+       }
+
+       public function testElggApiGettersValidTypeUsingTypesAsArrayPlural() {
+               $num = 2;
+               $types = $this->getRandomValidTypes($num);
+               $options = array(
+                       'types' => $types,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIsA($es, 'array');
+
+                       // one of object and one of group
+                       $this->assertIdentical(count($es), $num);
+
+                       foreach ($es as $e) {
+                               $this->assertTrue(in_array($e->getType(), $types));
+                       }
+       }
+
+       /*
+        * Test invalid types.
+        */
+       public function testElggApiGettersInvalidTypeUsingType() {
+               $type_arr = $this->getRandomInvalids();
+               $type = $type_arr[0];
+
+               $options = array(
+                       'type' => $type,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertFalse($es);
+       }
+
+       public function testElggApiGettersInvalidTypeUsingTypesAsString() {
+               $type_arr = $this->getRandomInvalids();
+               $type = $type_arr[0];
+
+               $options = array(
+                       'types' => $type,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIdentical($es, FALSE);
+       }
+
+       public function testElggApiGettersInvalidTypeUsingTypesAsArray() {
+               $type_arr = $this->getRandomInvalids();
+
+               $options = array(
+                       'types' => $type_arr,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIdentical($es, FALSE);
+       }
+
+       public function testElggApiGettersInvalidTypeUsingTypesAsArrayPlural() {
+               $type_arr = $this->getRandomInvalids(2);
+
+               $options = array(
+                       'types' => $type_arr,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIdentical($es, FALSE);
+       }
+
+
+       /*
+        * Test mixed valid and invalid types.
+        */
+
+
+       public function testElggApiGettersValidAndInvalidTypes() {
+               //@todo replace this with $this->getRandomMixedTypes().
+               $t = $this->getRandomValidTypes();
+               $valid = $t[0];
+
+               $t = $this->getRandomInvalids();
+               $invalid = $t[0];
+               $options = array(
+                       'types' => array($invalid, $valid),
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIsA($es, 'array');
+
+                       // should only ever return one object because of group by
+                       $this->assertIdentical(count($es), 1);
+                       $this->assertIdentical($es[0]->getType(), $valid);
+       }
+
+       public function testElggApiGettersValidAndInvalidTypesPlural() {
+               $valid_num = 2;
+               $invalid_num = 3;
+               $valid = $this->getRandomValidTypes($valid_num);
+               $invalid = $this->getRandomInvalids($invalid_num);
+
+               $types = array();
+               foreach ($valid as $t) {
+                       $types[] = $t;
+               }
+
+               foreach ($invalid as $t) {
+                       $types[] = $t;
+               }
+
+               shuffle($types);
+               $options = array(
+                       'types' => $types,
+                       'group_by' => 'e.type'
+                       );
+
+                       $es = elgg_get_entities($options);
+                       $this->assertIsA($es, 'array');
+
+                       // should only ever return one object because of group by
+                       $this->assertIdentical(count($es), $valid_num);
+                       foreach ($es as $e) {
+                               $this->assertTrue(in_array($e->getType(), $valid));
+                       }
+       }
+
+
+
+       /**************************************
+        * SUBTYPE TESTS
+        **************************************
+        *
+        * Here we can use the subtypes we created to test more finely.
+        * Subtypes are bound to types, so we must pass a type.
+        * This is where the fun logic starts.
+        */
+
+       public function testElggApiGettersValidSubtypeUsingSubtypeSingularType() {
+               $types = $this->getRandomValidTypes();
+               $subtypes = $this->getRandomValidSubtypes($types);
+               $subtype = $subtypes[0];
+
+               $options = array(
+                       'types' => $types,
+                       'subtype' => $subtype
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               $this->assertIdentical(count($es), 1);
+               foreach ($es as $e) {
+                       $this->assertTrue(in_array($e->getType(), $types));
+                       $this->assertTrue(in_array($e->getSubtype(), $subtypes));
+               }
+       }
+
+       public function testElggApiGettersValidSubtypeUsingSubtypesAsStringSingularType() {
+               $types = $this->getRandomValidTypes();
+               $subtypes = $this->getRandomValidSubtypes($types);
+               $subtype = $subtypes[0];
+
+               $options = array(
+                       'types' => $types,
+                       'subtypes' => $subtype
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               $this->assertIdentical(count($es), 1);
+               foreach ($es as $e) {
+                       $this->assertTrue(in_array($e->getType(), $types));
+                       $this->assertTrue(in_array($e->getSubtype(), $subtypes));
+               }
+       }
+
+       public function testElggApiGettersValidSubtypeUsingSubtypesAsArraySingularType() {
+               $types = $this->getRandomValidTypes();
+               $subtypes = $this->getRandomValidSubtypes($types);
+
+               $options = array(
+                       'types' => $types,
+                       'subtypes' => $subtypes
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               $this->assertIdentical(count($es), 1);
+               foreach ($es as $e) {
+                       $this->assertTrue(in_array($e->getType(), $types));
+                       $this->assertTrue(in_array($e->getSubtype(), $subtypes));
+               }
+       }
+
+       public function testElggApiGettersValidSubtypeUsingPluralSubtypesSingularType() {
+               $subtype_num = 2;
+               $types = $this->getRandomValidTypes();
+               $subtypes = $this->getRandomValidSubtypes($types, $subtype_num);
+
+               $options = array(
+                       'types' => $types,
+                       'subtypes' => $subtypes
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               $this->assertIdentical(count($es), $subtype_num);
+               foreach ($es as $e) {
+                       $this->assertTrue(in_array($e->getType(), $types));
+                       $this->assertTrue(in_array($e->getSubtype(), $subtypes));
+               }
+       }
+
+
+       /*
+       Because we're looking for type OR subtype (sorta)
+       it's possible that we've pulled in entities that aren't
+       of the subtype we've requested.
+       THIS COMBINATION MAKES LITTLE SENSE.
+       There is no mechanism in elgg to retrieve a subtype without a type, so
+       this combo gets trimmed down to only including subtypes that are valid to
+       each particular type.
+       FOR THE LOVE OF ALL GOOD PLEASE JUST USE TYPE_SUBTYPE_PAIRS!
+        */
+       public function testElggApiGettersValidSubtypeUsingPluralSubtypesPluralTypes() {
+               $type_num = 2;
+               $subtype_num = 2;
+               $types = $this->getRandomValidTypes($type_num);
+               $subtypes = $this->getRandomValidSubtypes($types, $subtype_num);
+
+               $options = array(
+                       'types' => $types,
+                       'subtypes' => $subtypes
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               // this will unset all invalid subtypes for each type that that only
+               // one entity exists of each.
+               $this->assertIdentical(count($es), $subtype_num);
+               foreach ($es as $e) {
+                       // entities must at least be in the type.
+                       $this->assertTrue(in_array($e->getType(), $types));
+
+                       // test that this is a valid subtype for the entity type.
+                       $this->assertTrue(in_array($e->getSubtype(), $this->subtypes[$e->getType()]));
+               }
+       }
+
+       public function testElggApiGettersInvalidSubtypeUsingPluralSubtypesPluralTypes() {
+               $type_num = 2;
+               $subtype_num = 2;
+               $types = $this->getRandomValidTypes($type_num);
+               $subtypes = $this->getRandomInvalids($subtype_num);
+
+               $options = array(
+                       'types' => $types,
+                       'subtypes' => $subtypes
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertFalse($es);
+       }
+
+       /*
+        * This combination will remove all invalid subtypes for this type.
+        */
+       public function testElggApiGettersValidSubtypeUsingPluralMixedSubtypesSingleType() {
+               $type_num = 1;
+               $subtype_num = 2;
+               $types = $this->getRandomValidTypes($type_num);
+
+
+               //@todo replace this with $this->getRandomMixedSubtypes()
+               // we want this to return an invalid subtype for the returned type.
+               $subtype_types = $types;
+               $i = 1;
+               while ($i <= $subtype_num) {
+                       $type = $this->types[$i-1];
+
+                       if (!in_array($type, $subtype_types)) {
+                               $subtype_types[] = $type;
+                       }
+                       $i++;
+               }
+
+               $subtypes = $this->getRandomValidSubtypes($subtype_types, $type_num);
+
+               $options = array(
+                       'types' => $types,
+                       'subtypes' => $subtypes
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               // this will unset all invalid subtypes for each type that that only
+               // one entity exists of each.
+               $this->assertIdentical(count($es), $type_num);
+               foreach ($es as $e) {
+                       // entities must at least be in the type.
+                       $this->assertTrue(in_array($e->getType(), $types));
+
+                       // test that this is a valid subtype for the entity type.
+                       $this->assertTrue(in_array($e->getSubtype(), $this->subtypes[$e->getType()]));
+               }
+       }
+
+
+       /***************************
+        * TYPE_SUBTYPE_PAIRS
+        ***************************/
+
+
+       public function testElggApiGettersTSPValidTypeValidSubtype() {
+               $type_num = 1;
+               $subtype_num = 1;
+               $types = $this->getRandomValidTypes($type_num);
+               $subtypes = $this->getRandomValidSubtypes($types, $subtype_num);
+
+               $pair = array($types[0] => $subtypes[0]);
+
+               $options = array(
+                       'type_subtype_pairs' => $pair
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               $this->assertIdentical(count($es), $type_num);
+               foreach ($es as $e) {
+                       $this->assertTrue(in_array($e->getType(), $types));
+                       $this->assertTrue(in_array($e->getSubtype(), $subtypes));
+               }
+       }
+
+       public function testElggApiGettersTSPValidTypeValidPluralSubtype() {
+               $type_num = 1;
+               $subtype_num = 3;
+               $types = $this->getRandomValidTypes($type_num);
+               $subtypes = $this->getRandomValidSubtypes($types, $subtype_num);
+
+               $pair = array($types[0] => $subtypes);
+
+               $options = array(
+                       'type_subtype_pairs' => $pair
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               $this->assertIdentical(count($es), $subtype_num);
+               foreach ($es as $e) {
+                       $this->assertTrue(in_array($e->getType(), $types));
+                       $this->assertTrue(in_array($e->getSubtype(), $subtypes));
+               }
+       }
+
+       public function testElggApiGettersTSPValidTypeInvalidSubtype() {
+               $type_num = 1;
+               $subtype_num = 1;
+               $types = $this->getRandomValidTypes($type_num);
+               $subtypes = $this->getRandomInvalids($subtype_num);
+
+               $pair = array($types[0] => $subtypes[0]);
+
+               $options = array(
+                       'type_subtype_pairs' => $pair
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertFalse($es);
+       }
+
+       public function testElggApiGettersTSPValidTypeInvalidPluralSubtypes() {
+               $type_num = 1;
+               $subtype_num = 2;
+               $types = $this->getRandomValidTypes($type_num);
+               $subtypes = $this->getRandomInvalids($subtype_num);
+
+               $pair = array($types[0] => $subtypes);
+
+               $options = array(
+                       'type_subtype_pairs' => $pair
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertFalse($es);
+       }
+
+       public function testElggApiGettersTSPValidTypeMixedPluralSubtype() {
+               $type_num = 1;
+               $valid_subtype_num = 2;
+               $types = $this->getRandomValidTypes($type_num);
+               $valid = $this->getRandomValidSubtypes($types, $valid_subtype_num);
+               $invalid = $this->getRandomInvalids();
+
+               $subtypes = array_merge($valid, $invalid);
+               shuffle($subtypes);
+
+               $pair = array($types[0] => $subtypes);
+
+               $options = array(
+                       'type_subtype_pairs' => $pair
+               );
+
+               $es = elgg_get_entities($options);
+               $this->assertIsA($es, 'array');
+
+               $this->assertIdentical(count($es), $valid_subtype_num);
+               foreach ($es as $e) {
+                       $this->assertTrue(in_array($e->getType(), $types));
+                       $this->assertTrue(in_array($e->getSubtype(), $valid));
+               }
+       }
+
+
+}