summaryrefslogtreecommitdiff
path: root/kolab.org/www/drupal-7.26/modules/simpletest/tests
diff options
context:
space:
mode:
Diffstat (limited to 'kolab.org/www/drupal-7.26/modules/simpletest/tests')
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/actions.test126
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.install11
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.module95
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax.test530
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_forms_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_forms_test.module502
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_test.module79
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/batch.test403
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.callbacks.inc141
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.module513
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/bootstrap.test543
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/cache.test437
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/common.test2787
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.css2
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.module276
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.print.css2
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_cron_helper.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_cron_helper.module17
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_info.txt9
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.install221
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.module241
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.test4111
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test.module27
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test_dependency.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test_dependency.module17
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.module251
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.test338
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query.test1681
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query_access_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query_access_test.module54
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/error.test116
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/error_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/error_test.module65
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/file.test2782
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/file_test.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/file_test.module461
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/filetransfer.test168
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/filter_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/filter_test.module64
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/form.test1867
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/form_test.file.inc48
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/form_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/form_test.module1857
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/graph.test195
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/http.php32
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/https.php31
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/image.test507
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/image_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/image_test.module138
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/lock.test57
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/mail.test461
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/menu.test1740
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/menu_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/menu_test.module563
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/module.test304
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/module_test.file.inc13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/module_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/module_test.install42
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/module_test.module131
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/pager.test159
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/password.test60
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/path.test381
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/path_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/path_test.module23
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/ExampleTest.php18
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/psr_0_test/lib/Drupal/psr_0_test/Tests/Nested/NestedExampleTest.php18
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/psr_0_test/psr_0_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/psr_0_test/psr_0_test.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/registry.test142
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/requirements1_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/requirements1_test.install21
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/requirements1_test.module7
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/requirements2_test.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/requirements2_test.module7
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/schema.test384
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/session.test530
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/session_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/session_test.module192
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system.base.css6
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_dependencies_test.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_dependencies_test.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_incompatible_core_version_dependencies_test.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_incompatible_core_version_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_incompatible_core_version_test.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.info14
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_incompatible_module_version_dependencies_test.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_incompatible_module_version_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_incompatible_module_version_test.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_test.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/system_test.module407
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/tablesort.test166
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/taxonomy_test.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/taxonomy_test.install34
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/taxonomy_test.module111
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/theme.test502
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/theme_test.inc15
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/theme_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/theme_test.module134
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/theme_test.template_test.tpl.php2
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/themes/test_basetheme/test_basetheme.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/themes/test_subtheme/test_subtheme.info13
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/themes/test_theme/template.php19
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/themes/test_theme/test_theme.info24
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/unicode.test305
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update.test115
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_script_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_script_test.install45
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_script_test.module18
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_1.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_1.install53
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_1.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_2.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_2.install53
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_2.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_3.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_3.install24
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/update_test_3.module1
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.bare.database.php8131
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.comments.database.php40
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.duplicate-permission.database.php8
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.filled.database.php20384
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.forum.database.php274
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.locale.database.php276
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.menu.database.php202
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.node_type_broken.database.php34
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.translatable.database.php125
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.trigger.database.php82
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.upload.database.php449
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.user-no-password-token.database.php10
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-6.user-password-token.database.php55
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-7.aggregator.database.php149
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-7.bare.minimal.database.php.gzbin0 -> 39843 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-7.bare.standard_all.database.php.gzbin0 -> 77424 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-7.field.database.php16
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-7.filled.minimal.database.php.gzbin0 -> 41805 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-7.filled.standard_all.database.php.gzbin0 -> 97603 bytes
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/drupal-7.trigger.database.php28
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/update.aggregator.test47
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/update.field.test61
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/update.trigger.test37
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/update.user.test35
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.comment.test32
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.filter.test55
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.forum.test64
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.locale.test143
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.menu.test83
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.node.test148
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.poll.test66
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.taxonomy.test201
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.test737
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.translatable.test51
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.trigger.test39
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.upload.test83
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/upgrade/upgrade.user.test92
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/url_alter_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/url_alter_test.install12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/url_alter_test.module71
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/xmlrpc.test244
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/xmlrpc_test.info12
-rw-r--r--kolab.org/www/drupal-7.26/modules/simpletest/tests/xmlrpc_test.module111
172 files changed, 62223 insertions, 0 deletions
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions.test
new file mode 100644
index 0000000..4d58b59
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions.test
@@ -0,0 +1,126 @@
+<?php
+
+class ActionsConfigurationTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Actions configuration',
+ 'description' => 'Tests complex actions configuration by adding, editing, and deleting a complex action.',
+ 'group' => 'Actions',
+ );
+ }
+
+ /**
+ * Test the configuration of advanced actions through the administration
+ * interface.
+ */
+ function testActionConfiguration() {
+ // Create a user with permission to view the actions administration pages.
+ $user = $this->drupalCreateUser(array('administer actions'));
+ $this->drupalLogin($user);
+
+ // Make a POST request to admin/config/system/actions/manage.
+ $edit = array();
+ $edit['action'] = drupal_hash_base64('system_goto_action');
+ $this->drupalPost('admin/config/system/actions/manage', $edit, t('Create'));
+
+ // Make a POST request to the individual action configuration page.
+ $edit = array();
+ $action_label = $this->randomName();
+ $edit['actions_label'] = $action_label;
+ $edit['url'] = 'admin';
+ $this->drupalPost('admin/config/system/actions/configure/' . drupal_hash_base64('system_goto_action'), $edit, t('Save'));
+
+ // Make sure that the new complex action was saved properly.
+ $this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully saved the complex action."));
+ $this->assertText($action_label, t("Make sure the action label appears on the configuration page after we've saved the complex action."));
+
+ // Make another POST request to the action edit page.
+ $this->clickLink(t('configure'));
+ preg_match('|admin/config/system/actions/configure/(\d+)|', $this->getUrl(), $matches);
+ $aid = $matches[1];
+ $edit = array();
+ $new_action_label = $this->randomName();
+ $edit['actions_label'] = $new_action_label;
+ $edit['url'] = 'admin';
+ $this->drupalPost(NULL, $edit, t('Save'));
+
+ // Make sure that the action updated properly.
+ $this->assertText(t('The action has been successfully saved.'), t("Make sure we get a confirmation that we've successfully updated the complex action."));
+ $this->assertNoText($action_label, t("Make sure the old action label does NOT appear on the configuration page after we've updated the complex action."));
+ $this->assertText($new_action_label, t("Make sure the action label appears on the configuration page after we've updated the complex action."));
+
+ // Make sure that deletions work properly.
+ $this->clickLink(t('delete'));
+ $edit = array();
+ $this->drupalPost("admin/config/system/actions/delete/$aid", $edit, t('Delete'));
+
+ // Make sure that the action was actually deleted.
+ $this->assertRaw(t('Action %action was deleted', array('%action' => $new_action_label)), t('Make sure that we get a delete confirmation message.'));
+ $this->drupalGet('admin/config/system/actions/manage');
+ $this->assertNoText($new_action_label, t("Make sure the action label does not appear on the overview page after we've deleted the action."));
+ $exists = db_query('SELECT aid FROM {actions} WHERE callback = :callback', array(':callback' => 'drupal_goto_action'))->fetchField();
+ $this->assertFalse($exists, t('Make sure the action is gone from the database after being deleted.'));
+ }
+}
+
+/**
+ * Test actions executing in a potential loop, and make sure they abort properly.
+ */
+class ActionLoopTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Actions executing in a potentially infinite loop',
+ 'description' => 'Tests actions executing in a loop, and makes sure they abort properly.',
+ 'group' => 'Actions',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('dblog', 'trigger', 'actions_loop_test');
+ }
+
+ /**
+ * Set up a loop with 3 - 12 recursions, and see if it aborts properly.
+ */
+ function testActionLoop() {
+ $user = $this->drupalCreateUser(array('administer actions'));
+ $this->drupalLogin($user);
+
+ $hash = drupal_hash_base64('actions_loop_test_log');
+ $edit = array('aid' => $hash);
+ $this->drupalPost('admin/structure/trigger/actions_loop_test', $edit, t('Assign'));
+
+ // Delete any existing watchdog messages to clear the plethora of
+ // "Action added" messages from when Drupal was installed.
+ db_delete('watchdog')->execute();
+ // To prevent this test from failing when xdebug is enabled, the maximum
+ // recursion level should be kept low enough to prevent the xdebug
+ // infinite recursion protection mechanism from aborting the request.
+ // See http://drupal.org/node/587634.
+ variable_set('actions_max_stack', 7);
+ $this->triggerActions();
+ }
+
+ /**
+ * Create an infinite loop by causing a watchdog message to be set,
+ * which causes the actions to be triggered again, up to actions_max_stack
+ * times.
+ */
+ protected function triggerActions() {
+ $this->drupalGet('<front>', array('query' => array('trigger_actions_on_watchdog' => TRUE)));
+ $expected = array();
+ $expected[] = 'Triggering action loop';
+ for ($i = 1; $i <= variable_get('actions_max_stack', 35); $i++) {
+ $expected[] = "Test log #$i";
+ }
+ $expected[] = 'Stack overflow: too many calls to actions_do(). Aborting to prevent infinite recursion.';
+
+ $result = db_query("SELECT message FROM {watchdog} WHERE type = 'actions_loop_test' OR type = 'actions' ORDER BY wid");
+ $loop_started = FALSE;
+ foreach ($result as $row) {
+ $expected_message = array_shift($expected);
+ $this->assertEqual($row->message, $expected_message, t('Expected message %expected, got %message.', array('%expected' => $expected_message, '%message' => $row->message)));
+ }
+ $this->assertTrue(empty($expected), t('All expected messages found.'));
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.info
new file mode 100644
index 0000000..dfd6d67
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.info
@@ -0,0 +1,12 @@
+name = Actions loop test
+description = Support module for action loop testing.
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.install b/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.install
new file mode 100644
index 0000000..b22fd85
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.install
@@ -0,0 +1,11 @@
+<?php
+
+/**
+ * Implements hook_install().
+ */
+function actions_loop_test_install() {
+ db_update('system')
+ ->fields(array('weight' => 1))
+ ->condition('name', 'actions_loop_test')
+ ->execute();
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.module
new file mode 100644
index 0000000..261cb80
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/actions_loop_test.module
@@ -0,0 +1,95 @@
+<?php
+
+/**
+ * Implements hook_trigger_info().
+ */
+function actions_loop_test_trigger_info() {
+ return array(
+ 'actions_loop_test' => array(
+ 'watchdog' => array(
+ 'label' => t('When a message is logged'),
+ ),
+ ),
+ );
+}
+
+/**
+ * Implements hook_watchdog().
+ */
+function actions_loop_test_watchdog(array $log_entry) {
+ // If the triggering actions are not explicitly enabled, abort.
+ if (empty($_GET['trigger_actions_on_watchdog'])) {
+ return;
+ }
+ // Get all the action ids assigned to the trigger on the watchdog hook's
+ // "run" event.
+ $aids = trigger_get_assigned_actions('watchdog');
+ // We can pass in any applicable information in $context. There isn't much in
+ // this case, but we'll pass in the hook name as the bare minimum.
+ $context = array(
+ 'hook' => 'watchdog',
+ );
+ // Fire the actions on the associated object ($log_entry) and the context
+ // variable.
+ actions_do(array_keys($aids), $log_entry, $context);
+}
+
+/**
+ * Implements hook_init().
+ */
+function actions_loop_test_init() {
+ if (!empty($_GET['trigger_actions_on_watchdog'])) {
+ watchdog_skip_semaphore('actions_loop_test', 'Triggering action loop');
+ }
+}
+
+/**
+ * Implements hook_action_info().
+ */
+function actions_loop_test_action_info() {
+ return array(
+ 'actions_loop_test_log' => array(
+ 'label' => t('Write a message to the log.'),
+ 'type' => 'system',
+ 'configurable' => FALSE,
+ 'triggers' => array('any'),
+ ),
+ );
+}
+
+/**
+ * Write a message to the log.
+ */
+function actions_loop_test_log() {
+ $count = &drupal_static(__FUNCTION__, 0);
+ $count++;
+ watchdog_skip_semaphore('actions_loop_test', "Test log #$count");
+}
+
+/**
+ * Replacement of the watchdog() function that eliminates the use of semaphores
+ * so that we can test the abortion of an action loop.
+ */
+function watchdog_skip_semaphore($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
+ global $user, $base_root;
+
+ // Prepare the fields to be logged
+ $log_entry = array(
+ 'type' => $type,
+ 'message' => $message,
+ 'variables' => $variables,
+ 'severity' => $severity,
+ 'link' => $link,
+ 'user' => $user,
+ 'uid' => isset($user->uid) ? $user->uid : 0,
+ 'request_uri' => $base_root . request_uri(),
+ 'referer' => $_SERVER['HTTP_REFERER'],
+ 'ip' => ip_address(),
+ 'timestamp' => REQUEST_TIME,
+ );
+
+ // Call the logging hooks to log/process the message
+ foreach (module_implements('watchdog') as $module) {
+ module_invoke($module, 'watchdog', $log_entry);
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax.test
new file mode 100644
index 0000000..664d520
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax.test
@@ -0,0 +1,530 @@
+<?php
+
+class AJAXTestCase extends DrupalWebTestCase {
+ function setUp() {
+ $modules = func_get_args();
+ if (isset($modules[0]) && is_array($modules[0])) {
+ $modules = $modules[0];
+ }
+ parent::setUp(array_unique(array_merge(array('ajax_test', 'ajax_forms_test'), $modules)));
+ }
+
+ /**
+ * Assert that a command with the required properties exists within the array of Ajax commands returned by the server.
+ *
+ * The Ajax framework, via the ajax_deliver() and ajax_render() functions,
+ * returns an array of commands. This array sometimes includes commands
+ * automatically provided by the framework in addition to commands returned by
+ * a particular page callback. During testing, we're usually interested that a
+ * particular command is present, and don't care whether other commands
+ * precede or follow the one we're interested in. Additionally, the command
+ * we're interested in may include additional data that we're not interested
+ * in. Therefore, this function simply asserts that one of the commands in
+ * $haystack contains all of the keys and values in $needle. Furthermore, if
+ * $needle contains a 'settings' key with an array value, we simply assert
+ * that all keys and values within that array are present in the command we're
+ * checking, and do not consider it a failure if the actual command contains
+ * additional settings that aren't part of $needle.
+ *
+ * @param $haystack
+ * An array of Ajax commands returned by the server.
+ * @param $needle
+ * Array of info we're expecting in one of those commands.
+ * @param $message
+ * An assertion message.
+ */
+ protected function assertCommand($haystack, $needle, $message) {
+ $found = FALSE;
+ foreach ($haystack as $command) {
+ // If the command has additional settings that we're not testing for, do
+ // not consider that a failure.
+ if (isset($command['settings']) && is_array($command['settings']) && isset($needle['settings']) && is_array($needle['settings'])) {
+ $command['settings'] = array_intersect_key($command['settings'], $needle['settings']);
+ }
+ // If the command has additional data that we're not testing for, do not
+ // consider that a failure. Also, == instead of ===, because we don't
+ // require the key/value pairs to be in any particular order
+ // (http://www.php.net/manual/en/language.operators.array.php).
+ if (array_intersect_key($command, $needle) == $needle) {
+ $found = TRUE;
+ break;
+ }
+ }
+ $this->assertTrue($found, $message);
+ }
+}
+
+/**
+ * Tests primary Ajax framework functions.
+ */
+class AJAXFrameworkTestCase extends AJAXTestCase {
+ protected $profile = 'testing';
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'AJAX framework',
+ 'description' => 'Performs tests on AJAX framework functions.',
+ 'group' => 'AJAX',
+ );
+ }
+
+ /**
+ * Test that ajax_render() returns JavaScript settings generated during the page request.
+ *
+ * @todo Add tests to ensure that ajax_render() returns commands for new CSS
+ * and JavaScript files to be loaded by the page. See
+ * http://drupal.org/node/561858.
+ */
+ function testAJAXRender() {
+ $commands = $this->drupalGetAJAX('ajax-test/render');
+
+ // Verify that there is a command to load settings added with
+ // drupal_add_js().
+ $expected = array(
+ 'command' => 'settings',
+ 'settings' => array('basePath' => base_path(), 'ajax' => 'test'),
+ );
+ $this->assertCommand($commands, $expected, t('ajax_render() loads settings added with drupal_add_js().'));
+
+ // Verify that Ajax settings are loaded for #type 'link'.
+ $this->drupalGet('ajax-test/link');
+ $settings = $this->drupalGetSettings();
+ $this->assertEqual($settings['ajax']['ajax-link']['url'], url('filter/tips'));
+ $this->assertEqual($settings['ajax']['ajax-link']['wrapper'], 'block-system-main');
+ }
+
+ /**
+ * Test behavior of ajax_render_error().
+ */
+ function testAJAXRenderError() {
+ // Verify default error message.
+ $commands = $this->drupalGetAJAX('ajax-test/render-error');
+ $expected = array(
+ 'command' => 'alert',
+ 'text' => t('An error occurred while handling the request: The server received invalid input.'),
+ );
+ $this->assertCommand($commands, $expected, t('ajax_render_error() invokes alert command.'));
+
+ // Verify custom error message.
+ $edit = array(
+ 'message' => 'Custom error message.',
+ );
+ $commands = $this->drupalGetAJAX('ajax-test/render-error', array('query' => $edit));
+ $expected = array(
+ 'command' => 'alert',
+ 'text' => $edit['message'],
+ );
+ $this->assertCommand($commands, $expected, t('Custom error message is output.'));
+ }
+
+ /**
+ * Test that new JavaScript and CSS files added during an AJAX request are returned.
+ */
+ function testLazyLoad() {
+ $expected = array(
+ 'setting_name' => 'ajax_forms_test_lazy_load_form_submit',
+ 'setting_value' => 'executed',
+ 'css' => drupal_get_path('module', 'system') . '/system.admin.css',
+ 'js' => drupal_get_path('module', 'system') . '/system.js',
+ );
+ // @todo D8: Add a drupal_css_defaults() helper function.
+ $expected_css_html = drupal_get_css(array($expected['css'] => array(
+ 'type' => 'file',
+ 'group' => CSS_DEFAULT,
+ 'weight' => 0,
+ 'every_page' => FALSE,
+ 'media' => 'all',
+ 'preprocess' => TRUE,
+ 'data' => $expected['css'],
+ 'browsers' => array('IE' => TRUE, '!IE' => TRUE),
+ )), TRUE);
+ $expected_js_html = drupal_get_js('header', array($expected['js'] => drupal_js_defaults($expected['js'])), TRUE);
+
+ // Get the base page.
+ $this->drupalGet('ajax_forms_test_lazy_load_form');
+ $original_settings = $this->drupalGetSettings();
+ $original_css = $original_settings['ajaxPageState']['css'];
+ $original_js = $original_settings['ajaxPageState']['js'];
+
+ // Verify that the base page doesn't have the settings and files that are to
+ // be lazy loaded as part of the next requests.
+ $this->assertTrue(!isset($original_settings[$expected['setting_name']]), t('Page originally lacks the %setting, as expected.', array('%setting' => $expected['setting_name'])));
+ $this->assertTrue(!isset($original_settings[$expected['css']]), t('Page originally lacks the %css file, as expected.', array('%css' => $expected['css'])));
+ $this->assertTrue(!isset($original_settings[$expected['js']]), t('Page originally lacks the %js file, as expected.', array('%js' => $expected['js'])));
+
+ // Submit the AJAX request without triggering files getting added.
+ $commands = $this->drupalPostAJAX(NULL, array('add_files' => FALSE), array('op' => t('Submit')));
+ $new_settings = $this->drupalGetSettings();
+
+ // Verify the setting was not added when not expected.
+ $this->assertTrue(!isset($new_settings['setting_name']), t('Page still lacks the %setting, as expected.', array('%setting' => $expected['setting_name'])));
+ // Verify a settings command does not add CSS or scripts to Drupal.settings
+ // and no command inserts the corresponding tags on the page.
+ $found_settings_command = FALSE;
+ $found_markup_command = FALSE;
+ foreach ($commands as $command) {
+ if ($command['command'] == 'settings' && (array_key_exists('css', $command['settings']['ajaxPageState']) || array_key_exists('js', $command['settings']['ajaxPageState']))) {
+ $found_settings_command = TRUE;
+ }
+ if (isset($command['data']) && ($command['data'] == $expected_js_html || $command['data'] == $expected_css_html)) {
+ $found_markup_command = TRUE;
+ }
+ }
+ $this->assertFalse($found_settings_command, t('Page state still lacks the %css and %js files, as expected.', array('%css' => $expected['css'], '%js' => $expected['js'])));
+ $this->assertFalse($found_markup_command, t('Page still lacks the %css and %js files, as expected.', array('%css' => $expected['css'], '%js' => $expected['js'])));
+
+ // Submit the AJAX request and trigger adding files.
+ $commands = $this->drupalPostAJAX(NULL, array('add_files' => TRUE), array('op' => t('Submit')));
+ $new_settings = $this->drupalGetSettings();
+ $new_css = $new_settings['ajaxPageState']['css'];
+ $new_js = $new_settings['ajaxPageState']['js'];
+
+ // Verify the expected setting was added.
+ $this->assertIdentical($new_settings[$expected['setting_name']], $expected['setting_value'], t('Page now has the %setting.', array('%setting' => $expected['setting_name'])));
+
+ // Verify the expected CSS file was added, both to Drupal.settings, and as
+ // an AJAX command for inclusion into the HTML.
+ $this->assertEqual($new_css, $original_css + array($expected['css'] => 1), t('Page state now has the %css file.', array('%css' => $expected['css'])));
+ $this->assertCommand($commands, array('data' => $expected_css_html), t('Page now has the %css file.', array('%css' => $expected['css'])));
+
+ // Verify the expected JS file was added, both to Drupal.settings, and as
+ // an AJAX command for inclusion into the HTML. By testing for an exact HTML
+ // string containing the SCRIPT tag, we also ensure that unexpected
+ // JavaScript code, such as a jQuery.extend() that would potentially clobber
+ // rather than properly merge settings, didn't accidentally get added.
+ $this->assertEqual($new_js, $original_js + array($expected['js'] => 1), t('Page state now has the %js file.', array('%js' => $expected['js'])));
+ $this->assertCommand($commands, array('data' => $expected_js_html), t('Page now has the %js file.', array('%js' => $expected['js'])));
+ }
+
+ /**
+ * Tests that overridden CSS files are not added during lazy load.
+ */
+ function testLazyLoadOverriddenCSS() {
+ // The test theme overrides system.base.css without an implementation,
+ // thereby removing it.
+ theme_enable(array('test_theme'));
+ variable_set('theme_default', 'test_theme');
+
+ // This gets the form, and emulates an Ajax submission on it, including
+ // adding markup to the HEAD and BODY for any lazy loaded JS/CSS files.
+ $this->drupalPostAJAX('ajax_forms_test_lazy_load_form', array('add_files' => TRUE), array('op' => t('Submit')));
+
+ // Verify that the resulting HTML does not load the overridden CSS file.
+ // We add a "?" to the assertion, because Drupal.settings may include
+ // information about the file; we only really care about whether it appears
+ // in a LINK or STYLE tag, for which Drupal always adds a query string for
+ // cache control.
+ $this->assertNoText('system.base.css?', 'Ajax lazy loading does not add overridden CSS files.');
+ }
+}
+
+/**
+ * Tests Ajax framework commands.
+ */
+class AJAXCommandsTestCase extends AJAXTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'AJAX commands',
+ 'description' => 'Performs tests on AJAX framework commands.',
+ 'group' => 'AJAX',
+ );
+ }
+
+ /**
+ * Test the various Ajax Commands.
+ */
+ function testAJAXCommands() {
+ $form_path = 'ajax_forms_test_ajax_commands_form';
+ $web_user = $this->drupalCreateUser(array('access content'));
+ $this->drupalLogin($web_user);
+
+ $edit = array();
+
+ // Tests the 'after' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'After': Click to put something after the div")));
+ $expected = array(
+ 'command' => 'insert',
+ 'method' => 'after',
+ 'data' => 'This will be placed after',
+ );
+ $this->assertCommand($commands, $expected, "'after' AJAX command issued with correct data");
+
+ // Tests the 'alert' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Alert': Click to alert")));
+ $expected = array(
+ 'command' => 'alert',
+ 'text' => 'Alert',
+ );
+ $this->assertCommand($commands, $expected, "'alert' AJAX Command issued with correct text");
+
+ // Tests the 'append' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'Append': Click to append something")));
+ $expected = array(
+ 'command' => 'insert',
+ 'method' => 'append',
+ 'data' => 'Appended text',
+ );
+ $this->assertCommand($commands, $expected, "'append' AJAX command issued with correct data");
+
+ // Tests the 'before' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'before': Click to put something before the div")));
+ $expected = array(
+ 'command' => 'insert',
+ 'method' => 'before',
+ 'data' => 'Before text',
+ );
+ $this->assertCommand($commands, $expected, "'before' AJAX command issued with correct data");
+
+ // Tests the 'changed' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed.")));
+ $expected = array(
+ 'command' => 'changed',
+ 'selector' => '#changed_div',
+ );
+ $this->assertCommand($commands, $expected, "'changed' AJAX command issued with correct selector");
+
+ // Tests the 'changed' command using the second argument.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX changed: Click to mark div changed with asterisk.")));
+ $expected = array(
+ 'command' => 'changed',
+ 'selector' => '#changed_div',
+ 'asterisk' => '#changed_div_mark_this',
+ );
+ $this->assertCommand($commands, $expected, "'changed' AJAX command (with asterisk) issued with correct selector");
+
+ // Tests the 'css' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("Set the the '#box' div to be blue.")));
+ $expected = array(
+ 'command' => 'css',
+ 'selector' => '#css_div',
+ 'argument' => array('background-color' => 'blue'),
+ );
+ $this->assertCommand($commands, $expected, "'css' AJAX command issued with correct selector");
+
+ // Tests the 'data' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX data command: Issue command.")));
+ $expected = array(
+ 'command' => 'data',
+ 'name' => 'testkey',
+ 'value' => 'testvalue',
+ );
+ $this->assertCommand($commands, $expected, "'data' AJAX command issued with correct key and value");
+
+ // Tests the 'invoke' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX invoke command: Invoke addClass() method.")));
+ $expected = array(
+ 'command' => 'invoke',
+ 'method' => 'addClass',
+ 'arguments' => array('error'),
+ );
+ $this->assertCommand($commands, $expected, "'invoke' AJAX command issued with correct method and argument");
+
+ // Tests the 'html' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX html: Replace the HTML in a selector.")));
+ $expected = array(
+ 'command' => 'insert',
+ 'method' => 'html',
+ 'data' => 'replacement text',
+ );
+ $this->assertCommand($commands, $expected, "'html' AJAX command issued with correct data");
+
+ // Tests the 'insert' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX insert: Let client insert based on #ajax['method'].")));
+ $expected = array(
+ 'command' => 'insert',
+ 'data' => 'insert replacement text',
+ );
+ $this->assertCommand($commands, $expected, "'insert' AJAX command issued with correct data");
+
+ // Tests the 'prepend' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'prepend': Click to prepend something")));
+ $expected = array(
+ 'command' => 'insert',
+ 'method' => 'prepend',
+ 'data' => 'prepended text',
+ );
+ $this->assertCommand($commands, $expected, "'prepend' AJAX command issued with correct data");
+
+ // Tests the 'remove' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'remove': Click to remove text")));
+ $expected = array(
+ 'command' => 'remove',
+ 'selector' => '#remove_text',
+ );
+ $this->assertCommand($commands, $expected, "'remove' AJAX command issued with correct command and selector");
+
+ // Tests the 'restripe' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'restripe' command")));
+ $expected = array(
+ 'command' => 'restripe',
+ 'selector' => '#restripe_table',
+ );
+ $this->assertCommand($commands, $expected, "'restripe' AJAX command issued with correct selector");
+
+ // Tests the 'settings' command.
+ $commands = $this->drupalPostAJAX($form_path, $edit, array('op' => t("AJAX 'settings' command")));
+ $expected = array(
+ 'command' => 'settings',
+ 'settings' => array('ajax_forms_test' => array('foo' => 42)),
+ );
+ $this->assertCommand($commands, $expected, "'settings' AJAX command issued with correct data");
+ }
+}
+
+/**
+ * Test that $form_state['values'] is properly delivered to $ajax['callback'].
+ */
+class AJAXFormValuesTestCase extends AJAXTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'AJAX command form values',
+ 'description' => 'Tests that form values are properly delivered to AJAX callbacks.',
+ 'group' => 'AJAX',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ $this->web_user = $this->drupalCreateUser(array('access content'));
+ $this->drupalLogin($this->web_user);
+ }
+
+ /**
+ * Create a simple form, then POST to system/ajax to change to it.
+ */
+ function testSimpleAJAXFormValue() {
+ // Verify form values of a select element.
+ foreach (array('red', 'green', 'blue') as $item) {
+ $edit = array(
+ 'select' => $item,
+ );
+ $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'select');
+ $expected = array(
+ 'command' => 'data',
+ 'value' => $item,
+ );
+ $this->assertCommand($commands, $expected, "verification of AJAX form values from a selectbox issued with a correct value");
+ }
+
+ // Verify form values of a checkbox element.
+ foreach (array(FALSE, TRUE) as $item) {
+ $edit = array(
+ 'checkbox' => $item,
+ );
+ $commands = $this->drupalPostAJAX('ajax_forms_test_get_form', $edit, 'checkbox');
+ $expected = array(
+ 'command' => 'data',
+ 'value' => (int) $item,
+ );
+ $this->assertCommand($commands, $expected, "verification of AJAX form values from a checkbox issued with a correct value");
+ }
+ }
+}
+
+/**
+ * Tests that Ajax-enabled forms work when multiple instances of the same form are on a page.
+ */
+class AJAXMultiFormTestCase extends AJAXTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'AJAX multi form',
+ 'description' => 'Tests that AJAX-enabled forms work when multiple instances of the same form are on a page.',
+ 'group' => 'AJAX',
+ );
+ }
+
+ function setUp() {
+ parent::setUp(array('form_test'));
+
+ // Create a multi-valued field for 'page' nodes to use for Ajax testing.
+ $field_name = 'field_ajax_test';
+ $field = array(
+ 'field_name' => $field_name,
+ 'type' => 'text',
+ 'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+ );
+ field_create_field($field);
+ $instance = array(
+ 'field_name' => $field_name,
+ 'entity_type' => 'node',
+ 'bundle' => 'page',
+ );
+ field_create_instance($instance);
+
+ // Login a user who can create 'page' nodes.
+ $this->web_user = $this->drupalCreateUser(array('create page content'));
+ $this->drupalLogin($this->web_user);
+ }
+
+ /**
+ * Test that a page with the 'page_node_form' included twice works correctly.
+ */
+ function testMultiForm() {
+ // HTML IDs for elements within the field are potentially modified with
+ // each Ajax submission, but these variables are stable and help target the
+ // desired elements.
+ $field_name = 'field_ajax_test';
+ $field_xpaths = array(
+ 'page-node-form' => '//form[@id="page-node-form"]//div[contains(@class, "field-name-field-ajax-test")]',
+ 'page-node-form--2' => '//form[@id="page-node-form--2"]//div[contains(@class, "field-name-field-ajax-test")]',
+ );
+ $button_name = $field_name . '_add_more';
+ $button_value = t('Add another item');
+ $button_xpath_suffix = '//input[@name="' . $button_name . '"]';
+ $field_items_xpath_suffix = '//input[@type="text"]';
+
+ // Ensure the initial page contains both node forms and the correct number
+ // of field items and "add more" button for the multi-valued field within
+ // each form.
+ $this->drupalGet('form-test/two-instances-of-same-form');
+ foreach ($field_xpaths as $form_html_id => $field_xpath) {
+ $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == 1, t('Found the correct number of field items on the initial page.'));
+ $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button on the initial page.'));
+ }
+ $this->assertNoDuplicateIds(t('Initial page contains unique IDs'), 'Other');
+
+ // Submit the "add more" button of each form twice. After each corresponding
+ // page update, ensure the same as above.
+ foreach ($field_xpaths as $form_html_id => $field_xpath) {
+ for ($i = 0; $i < 2; $i++) {
+ $this->drupalPostAJAX(NULL, array(), array($button_name => $button_value), 'system/ajax', array(), array(), $form_html_id);
+ $this->assert(count($this->xpath($field_xpath . $field_items_xpath_suffix)) == $i+2, t('Found the correct number of field items after an AJAX submission.'));
+ $this->assertFieldByXPath($field_xpath . $button_xpath_suffix, NULL, t('Found the "add more" button after an AJAX submission.'));
+ $this->assertNoDuplicateIds(t('Updated page contains unique IDs'), 'Other');
+ }
+ }
+ }
+}
+
+/**
+ * Miscellaneous Ajax tests using ajax_test module.
+ */
+class AJAXElementValidation extends AJAXTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Miscellaneous AJAX tests',
+ 'description' => 'Various tests of AJAX behavior',
+ 'group' => 'AJAX',
+ );
+ }
+
+ /**
+ * Try to post an Ajax change to a form that has a validated element.
+ *
+ * The drivertext field is Ajax-enabled. An additional field is not, but
+ * is set to be a required field. In this test the required field is not
+ * filled in, and we want to see if the activation of the "drivertext"
+ * Ajax-enabled field fails due to the required field being empty.
+ */
+ function testAJAXElementValidation() {
+ $web_user = $this->drupalCreateUser();
+ $edit = array('drivertext' => t('some dumb text'));
+
+ // Post with 'drivertext' as the triggering element.
+ $post_result = $this->drupalPostAJAX('ajax_validation_test', $edit, 'drivertext');
+ // Look for a validation failure in the resultant JSON.
+ $this->assertNoText(t('Error message'), "No error message in resultant JSON");
+ $this->assertText('ajax_forms_test_validation_form_callback invoked', 'The correct callback was invoked');
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_forms_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_forms_test.info
new file mode 100644
index 0000000..67c45e2
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_forms_test.info
@@ -0,0 +1,12 @@
+name = "AJAX form test mock module"
+description = "Test for AJAX form calls."
+core = 7.x
+package = Testing
+version = VERSION
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_forms_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_forms_test.module
new file mode 100644
index 0000000..2840422
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_forms_test.module
@@ -0,0 +1,502 @@
+<?php
+
+/**
+ * @file
+ * Simpletest mock module for Ajax forms testing.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function ajax_forms_test_menu() {
+ $items = array();
+ $items['ajax_forms_test_get_form'] = array(
+ 'title' => 'AJAX forms simple form test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('ajax_forms_test_simple_form'),
+ 'access callback' => TRUE,
+ );
+ $items['ajax_forms_test_ajax_commands_form'] = array(
+ 'title' => 'AJAX forms AJAX commands test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('ajax_forms_test_ajax_commands_form'),
+ 'access callback' => TRUE,
+ );
+ $items['ajax_validation_test'] = array(
+ 'title' => 'AJAX Validation Test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('ajax_forms_test_validation_form'),
+ 'access callback' => TRUE,
+ );
+ $items['ajax_forms_test_lazy_load_form'] = array(
+ 'title' => 'AJAX forms lazy load test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('ajax_forms_test_lazy_load_form'),
+ 'access callback' => TRUE,
+ );
+ return $items;
+}
+
+
+/**
+ * A basic form used to test form_state['values'] during callback.
+ */
+function ajax_forms_test_simple_form($form, &$form_state) {
+ $form = array();
+ $form['select'] = array(
+ '#type' => 'select',
+ '#options' => array(
+ 'red' => 'red',
+ 'green' => 'green',
+ 'blue' => 'blue'),
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_simple_form_select_callback',
+ ),
+ '#suffix' => '<div id="ajax_selected_color">No color yet selected</div>',
+ );
+
+ $form['checkbox'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Test checkbox'),
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_simple_form_checkbox_callback',
+ ),
+ '#suffix' => '<div id="ajax_checkbox_value">No action yet</div>',
+ );
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('submit'),
+ );
+ return $form;
+}
+
+/**
+ * Ajax callback triggered by select.
+ */
+function ajax_forms_test_simple_form_select_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_html('#ajax_selected_color', $form_state['values']['select']);
+ $commands[] = ajax_command_data('#ajax_selected_color', 'form_state_value_select', $form_state['values']['select']);
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback triggered by checkbox.
+ */
+function ajax_forms_test_simple_form_checkbox_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_html('#ajax_checkbox_value', (int) $form_state['values']['checkbox']);
+ $commands[] = ajax_command_data('#ajax_checkbox_value', 'form_state_value_select', (int) $form_state['values']['checkbox']);
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+
+/**
+ * Form to display the Ajax Commands.
+ */
+function ajax_forms_test_ajax_commands_form($form, &$form_state) {
+ $form = array();
+
+ // Shows the 'after' command with a callback generating commands.
+ $form['after_command_example'] = array(
+ '#value' => t("AJAX 'After': Click to put something after the div"),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_after_callback',
+ ),
+ '#suffix' => '<div id="after_div">Something can be inserted after this</div>',
+ );
+
+ // Shows the 'alert' command.
+ $form['alert_command_example'] = array(
+ '#value' => t("AJAX 'Alert': Click to alert"),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_alert_callback',
+ ),
+ );
+
+ // Shows the 'append' command.
+ $form['append_command_example'] = array(
+ '#value' => t("AJAX 'Append': Click to append something"),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_append_callback',
+ ),
+ '#suffix' => '<div id="append_div">Append inside this div</div>',
+ );
+
+
+ // Shows the 'before' command.
+ $form['before_command_example'] = array(
+ '#value' => t("AJAX 'before': Click to put something before the div"),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_before_callback',
+ ),
+ '#suffix' => '<div id="before_div">Insert something before this.</div>',
+ );
+
+ // Shows the 'changed' command without asterisk.
+ $form['changed_command_example'] = array(
+ '#value' => t("AJAX changed: Click to mark div changed."),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_changed_callback',
+ ),
+ '#suffix' => '<div id="changed_div"> <div id="changed_div_mark_this">This div can be marked as changed or not.</div></div>',
+ );
+ // Shows the 'changed' command adding the asterisk.
+ $form['changed_command_asterisk_example'] = array(
+ '#value' => t("AJAX changed: Click to mark div changed with asterisk."),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_changed_asterisk_callback',
+ ),
+ );
+
+ // Shows the Ajax 'css' command.
+ $form['css_command_example'] = array(
+ '#value' => t("Set the the '#box' div to be blue."),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_css_callback',
+ ),
+ '#suffix' => '<div id="css_div" style="height: 50px; width: 50px; border: 1px solid black"> box</div>',
+ );
+
+
+ // Shows the Ajax 'data' command. But there is no use of this information,
+ // as this would require a javascript client to use the data.
+ $form['data_command_example'] = array(
+ '#value' => t("AJAX data command: Issue command."),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_data_callback',
+ ),
+ '#suffix' => '<div id="data_div">Data attached to this div.</div>',
+ );
+
+ // Shows the Ajax 'invoke' command.
+ $form['invoke_command_example'] = array(
+ '#value' => t("AJAX invoke command: Invoke addClass() method."),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_invoke_callback',
+ ),
+ '#suffix' => '<div id="invoke_div">Original contents</div>',
+ );
+
+ // Shows the Ajax 'html' command.
+ $form['html_command_example'] = array(
+ '#value' => t("AJAX html: Replace the HTML in a selector."),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_html_callback',
+ ),
+ '#suffix' => '<div id="html_div">Original contents</div>',
+ );
+
+ // Shows the Ajax 'insert' command.
+ $form['insert_command_example'] = array(
+ '#value' => t("AJAX insert: Let client insert based on #ajax['method']."),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_insert_callback',
+ 'method' => 'prepend',
+ ),
+ '#suffix' => '<div id="insert_div">Original contents</div>',
+ );
+
+ // Shows the Ajax 'prepend' command.
+ $form['prepend_command_example'] = array(
+ '#value' => t("AJAX 'prepend': Click to prepend something"),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_prepend_callback',
+ ),
+ '#suffix' => '<div id="prepend_div">Something will be prepended to this div. </div>',
+ );
+
+ // Shows the Ajax 'remove' command.
+ $form['remove_command_example'] = array(
+ '#value' => t("AJAX 'remove': Click to remove text"),
+ '#type' => 'submit',
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_remove_callback',
+ ),
+ '#suffix' => '<div id="remove_div"><div id="remove_text">text to be removed</div></div>',
+ );
+
+ // Shows the Ajax 'restripe' command.
+ $form['restripe_command_example'] = array(
+ '#type' => 'submit',
+ '#value' => t("AJAX 'restripe' command"),
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_restripe_callback',
+ ),
+ '#suffix' => '<div id="restripe_div">
+ <table id="restripe_table" style="border: 1px solid black" >
+ <tr id="table-first"><td>first row</td></tr>
+ <tr ><td>second row</td></tr>
+ </table>
+ </div>',
+ );
+
+ // Demonstrates the Ajax 'settings' command. The 'settings' command has
+ // nothing visual to "show", but it can be tested via SimpleTest and via
+ // Firebug.
+ $form['settings_command_example'] = array(
+ '#type' => 'submit',
+ '#value' => t("AJAX 'settings' command"),
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_advanced_commands_settings_callback',
+ ),
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Submit'),
+ );
+
+ return $form;
+}
+
+/**
+ * Ajax callback for 'after'.
+ */
+function ajax_forms_test_advanced_commands_after_callback($form, $form_state) {
+ $selector = '#after_div';
+
+ $commands = array();
+ $commands[] = ajax_command_after($selector, "This will be placed after");
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'alert'.
+ */
+function ajax_forms_test_advanced_commands_alert_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_alert("Alert");
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'append'.
+ */
+function ajax_forms_test_advanced_commands_append_callback($form, $form_state) {
+ $selector = '#append_div';
+ $commands = array();
+ $commands[] = ajax_command_append($selector, "Appended text");
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'before'.
+ */
+function ajax_forms_test_advanced_commands_before_callback($form, $form_state) {
+ $selector = '#before_div';
+
+ $commands = array();
+ $commands[] = ajax_command_before($selector, "Before text");
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'changed'.
+ */
+function ajax_forms_test_advanced_commands_changed_callback($form, $form_state) {
+ $commands[] = ajax_command_changed('#changed_div');
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+/**
+ * Ajax callback for 'changed' with asterisk marking inner div.
+ */
+function ajax_forms_test_advanced_commands_changed_asterisk_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_changed('#changed_div', '#changed_div_mark_this');
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'css'.
+ */
+function ajax_forms_test_advanced_commands_css_callback($form, $form_state) {
+ $selector = '#css_div';
+ $color = 'blue';
+
+ $commands = array();
+ $commands[] = ajax_command_css($selector, array('background-color' => $color));
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'data'.
+ */
+function ajax_forms_test_advanced_commands_data_callback($form, $form_state) {
+ $selector = '#data_div';
+
+ $commands = array();
+ $commands[] = ajax_command_data($selector, 'testkey', 'testvalue');
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'invoke'.
+ */
+function ajax_forms_test_advanced_commands_invoke_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_invoke('#invoke_div', 'addClass', array('error'));
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'html'.
+ */
+function ajax_forms_test_advanced_commands_html_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_html('#html_div', 'replacement text');
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'insert'.
+ */
+function ajax_forms_test_advanced_commands_insert_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_insert('#insert_div', 'insert replacement text');
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'prepend'.
+ */
+function ajax_forms_test_advanced_commands_prepend_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_prepend('#prepend_div', "prepended text");
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'remove'.
+ */
+function ajax_forms_test_advanced_commands_remove_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_remove('#remove_text');
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'restripe'.
+ */
+function ajax_forms_test_advanced_commands_restripe_callback($form, $form_state) {
+ $commands = array();
+ $commands[] = ajax_command_restripe('#restripe_table');
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * Ajax callback for 'settings'.
+ */
+function ajax_forms_test_advanced_commands_settings_callback($form, $form_state) {
+ $commands = array();
+ $setting['ajax_forms_test']['foo'] = 42;
+ $commands[] = ajax_command_settings($setting);
+ return array('#type' => 'ajax', '#commands' => $commands);
+}
+
+/**
+ * This form and its related submit and callback functions demonstrate
+ * not validating another form element when a single Ajax element is triggered.
+ *
+ * The "drivertext" element is an Ajax-enabled textfield, free-form.
+ * The "required_field" element is a textfield marked required.
+ *
+ * The correct behavior is that the Ajax-enabled drivertext element should
+ * be able to trigger without causing validation of the "required_field".
+ */
+function ajax_forms_test_validation_form($form, &$form_state) {
+
+ $form['drivertext'] = array(
+ '#title' => t('AJAX-enabled textfield.'),
+ '#description' => t("When this one AJAX-triggers and the spare required field is empty, you should not get an error."),
+ '#type' => 'textfield',
+ '#default_value' => !empty($form_state['values']['drivertext']) ? $form_state['values']['drivertext'] : "",
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_validation_form_callback',
+ 'wrapper' => 'message_area',
+ 'method' => 'replace',
+ ),
+ '#suffix' => '<div id="message_area"></div>',
+ );
+
+ $form['spare_required_field'] = array(
+ '#title' => t("Spare Required Field"),
+ '#type' => 'textfield',
+ '#required' => TRUE,
+ );
+
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Submit'),
+ );
+
+ return $form;
+}
+/**
+ * Submit handler for the validation form.
+ */
+function ajax_forms_test_validation_form_submit($form, $form_state) {
+ drupal_set_message(t("Validation form submitted"));
+}
+
+/**
+ * Ajax callback for the 'drivertext' element of the validation form.
+ */
+function ajax_forms_test_validation_form_callback($form, $form_state) {
+ drupal_set_message("ajax_forms_test_validation_form_callback invoked");
+ drupal_set_message(t("Callback: drivertext=%drivertext, spare_required_field=%spare_required_field", array('%drivertext' => $form_state['values']['drivertext'], '%spare_required_field' => $form_state['values']['spare_required_field'])));
+ return '<div id="message_area">ajax_forms_test_validation_form_callback at ' . date('c') . '</div>';
+}
+
+/**
+ * Form builder: Builds a form that triggers a simple AJAX callback.
+ */
+function ajax_forms_test_lazy_load_form($form, &$form_state) {
+ $form['add_files'] = array(
+ '#type' => 'checkbox',
+ '#default_value' => FALSE,
+ );
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Submit'),
+ '#ajax' => array(
+ 'callback' => 'ajax_forms_test_lazy_load_form_ajax',
+ ),
+ );
+ return $form;
+}
+
+/**
+ * Form submit handler: Adds JavaScript and CSS that wasn't on the original form.
+ */
+function ajax_forms_test_lazy_load_form_submit($form, &$form_state) {
+ if ($form_state['values']['add_files']) {
+ drupal_add_js(array('ajax_forms_test_lazy_load_form_submit' => 'executed'), 'setting');
+ drupal_add_css(drupal_get_path('module', 'system') . '/system.admin.css');
+ drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
+ }
+ $form_state['rebuild'] = TRUE;
+}
+
+/**
+ * AJAX callback for the ajax_forms_test_lazy_load_form() form.
+ *
+ * This function returns nothing, because all we're interested in testing is
+ * ajax_render() adding commands for JavaScript and CSS added during the page
+ * request, such as the ones added in ajax_forms_test_lazy_load_form_submit().
+ */
+function ajax_forms_test_lazy_load_form_ajax($form, &$form_state) {
+ return NULL;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_test.info
new file mode 100644
index 0000000..0dab7ed
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_test.info
@@ -0,0 +1,12 @@
+name = AJAX Test
+description = Support module for AJAX framework tests.
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_test.module
new file mode 100644
index 0000000..21be019
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/ajax_test.module
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * @file
+ * Helper module for Ajax framework tests.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function ajax_test_menu() {
+ $items['ajax-test/render'] = array(
+ 'title' => 'ajax_render',
+ 'page callback' => 'ajax_test_render',
+ 'delivery callback' => 'ajax_deliver',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['ajax-test/render-error'] = array(
+ 'title' => 'ajax_render_error',
+ 'page callback' => 'ajax_test_error',
+ 'delivery callback' => 'ajax_deliver',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['ajax-test/link'] = array(
+ 'title' => 'AJAX Link',
+ 'page callback' => 'ajax_test_link',
+ 'access callback' => TRUE,
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_system_theme_info().
+ */
+function ajax_test_system_theme_info() {
+ $themes['test_theme'] = drupal_get_path('module', 'ajax_test') . '/themes/test_theme/test_theme.info';
+ return $themes;
+}
+
+/**
+ * Menu callback; Return an element suitable for use by ajax_deliver().
+ *
+ * Additionally ensures that ajax_render() incorporates JavaScript settings
+ * generated during the page request by invoking drupal_add_js() with a dummy
+ * setting.
+ */
+function ajax_test_render() {
+ drupal_add_js(array('ajax' => 'test'), 'setting');
+ return array('#type' => 'ajax', '#commands' => array());
+}
+
+/**
+ * Menu callback; Returns Ajax element with #error property set.
+ */
+function ajax_test_error() {
+ $message = '';
+ if (!empty($_GET['message'])) {
+ $message = $_GET['message'];
+ }
+ return array('#type' => 'ajax', '#error' => $message);
+}
+
+/**
+ * Menu callback; Renders a #type link with #ajax.
+ */
+function ajax_test_link() {
+ $build['link'] = array(
+ '#type' => 'link',
+ '#title' => 'Show help',
+ '#href' => 'filter/tips',
+ '#ajax' => array(
+ 'wrapper' => 'block-system-main',
+ ),
+ );
+ return $build;
+}
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch.test
new file mode 100644
index 0000000..e41bc92
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch.test
@@ -0,0 +1,403 @@
+<?php
+
+/**
+ * @file
+ * Tests for the Batch API.
+ */
+
+/**
+ * Tests for the Batch API.
+ */
+class BatchProcessingTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Batch processing',
+ 'description' => 'Test batch processing in form and non-form workflow.',
+ 'group' => 'Batch API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('batch_test');
+ }
+
+ /**
+ * Test batches triggered outside of form submission.
+ */
+ function testBatchNoForm() {
+ // Displaying the page triggers batch 1.
+ $this->drupalGet('batch-test/no-form');
+ $this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+ }
+
+ /**
+ * Test batches defined in a form submit handler.
+ */
+ function testBatchForm() {
+ // Batch 0: no operation.
+ $edit = array('batch' => 'batch_0');
+ $this->drupalPost('batch-test/simple', $edit, 'Submit');
+ $this->assertBatchMessages($this->_resultMessages('batch_0'), t('Batch with no operation performed successfully.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+
+ // Batch 1: several simple operations.
+ $edit = array('batch' => 'batch_1');
+ $this->drupalPost('batch-test/simple', $edit, 'Submit');
+ $this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch with simple operations performed successfully.'));
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+
+ // Batch 2: one multistep operation.
+ $edit = array('batch' => 'batch_2');
+ $this->drupalPost('batch-test/simple', $edit, 'Submit');
+ $this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch with multistep operation performed successfully.'));
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+
+ // Batch 3: simple + multistep combined.
+ $edit = array('batch' => 'batch_3');
+ $this->drupalPost('batch-test/simple', $edit, 'Submit');
+ $this->assertBatchMessages($this->_resultMessages('batch_3'), t('Batch with simple and multistep operations performed successfully.'));
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_3'), t('Execution order was correct.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+
+ // Batch 4: nested batch.
+ $edit = array('batch' => 'batch_4');
+ $this->drupalPost('batch-test/simple', $edit, 'Submit');
+ $this->assertBatchMessages($this->_resultMessages('batch_4'), t('Nested batch performed successfully.'));
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_4'), t('Execution order was correct.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+ }
+
+ /**
+ * Test batches defined in a multistep form.
+ */
+ function testBatchFormMultistep() {
+ $this->drupalGet('batch-test/multistep');
+ $this->assertText('step 1', t('Form is displayed in step 1.'));
+
+ // First step triggers batch 1.
+ $this->drupalPost(NULL, array(), 'Submit');
+ $this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch for step 1 performed successfully.'));
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
+ $this->assertText('step 2', t('Form is displayed in step 2.'));
+
+ // Second step triggers batch 2.
+ $this->drupalPost(NULL, array(), 'Submit');
+ $this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch for step 2 performed successfully.'));
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+ }
+
+ /**
+ * Test batches defined in different submit handlers on the same form.
+ */
+ function testBatchFormMultipleBatches() {
+ // Batches 1, 2 and 3 are triggered in sequence by different submit
+ // handlers. Each submit handler modify the submitted 'value'.
+ $value = rand(0, 255);
+ $edit = array('value' => $value);
+ $this->drupalPost('batch-test/chained', $edit, 'Submit');
+ // Check that result messages are present and in the correct order.
+ $this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
+ // The stack contains execution order of batch callbacks and submit
+ // hanlders and logging of corresponding $form_state[{values'].
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+ }
+
+ /**
+ * Test batches defined in a programmatically submitted form.
+ *
+ * Same as above, but the form is submitted through drupal_form_execute().
+ */
+ function testBatchFormProgrammatic() {
+ // Batches 1, 2 and 3 are triggered in sequence by different submit
+ // handlers. Each submit handler modify the submitted 'value'.
+ $value = rand(0, 255);
+ $this->drupalGet('batch-test/programmatic/' . $value);
+ // Check that result messages are present and in the correct order.
+ $this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
+ // The stack contains execution order of batch callbacks and submit
+ // hanlders and logging of corresponding $form_state[{values'].
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
+ $this->assertText('Got out of a programmatic batched form.', t('Page execution continues normally.'));
+ }
+
+ /**
+ * Test that drupal_form_submit() can run within a batch operation.
+ */
+ function testDrupalFormSubmitInBatch() {
+ // Displaying the page triggers a batch that programmatically submits a
+ // form.
+ $value = rand(0, 255);
+ $this->drupalGet('batch-test/nested-programmatic/' . $value);
+ $this->assertEqual(batch_test_stack(), array('mock form submitted with value = ' . $value), t('drupal_form_submit() ran successfully within a batch operation.'));
+ }
+
+ /**
+ * Test batches that return $context['finished'] > 1 do in fact complete.
+ * See http://drupal.org/node/600836
+ */
+ function testBatchLargePercentage() {
+ // Displaying the page triggers batch 5.
+ $this->drupalGet('batch-test/large-percentage');
+ $this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
+ $this->assertEqual(batch_test_stack(), $this->_resultStack('batch_5'), t('Execution order was correct.'));
+ $this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
+ }
+
+
+ /**
+ * Will trigger a pass if the texts were found in order in the raw content.
+ *
+ * @param $texts
+ * Array of raw strings to look for .
+ * @param $message
+ * Message to display.
+ * @return
+ * TRUE on pass, FALSE on fail.
+ */
+ function assertBatchMessages($texts, $message) {
+ $pattern = '|' . implode('.*', $texts) .'|s';
+ return $this->assertPattern($pattern, $message);
+ }
+
+ /**
+ * Helper function: return expected execution stacks for the test batches.
+ */
+ function _resultStack($id, $value = 0) {
+ $stack = array();
+ switch ($id) {
+ case 'batch_1':
+ for ($i = 1; $i <= 10; $i++) {
+ $stack[] = "op 1 id $i";
+ }
+ break;
+
+ case 'batch_2':
+ for ($i = 1; $i <= 10; $i++) {
+ $stack[] = "op 2 id $i";
+ }
+ break;
+
+ case 'batch_3':
+ for ($i = 1; $i <= 5; $i++) {
+ $stack[] = "op 1 id $i";
+ }
+ for ($i = 1; $i <= 5; $i++) {
+ $stack[] = "op 2 id $i";
+ }
+ for ($i = 6; $i <= 10; $i++) {
+ $stack[] = "op 1 id $i";
+ }
+ for ($i = 6; $i <= 10; $i++) {
+ $stack[] = "op 2 id $i";
+ }
+ break;
+
+ case 'batch_4':
+ for ($i = 1; $i <= 5; $i++) {
+ $stack[] = "op 1 id $i";
+ }
+ $stack[] = 'setting up batch 2';
+ for ($i = 6; $i <= 10; $i++) {
+ $stack[] = "op 1 id $i";
+ }
+ $stack = array_merge($stack, $this->_resultStack('batch_2'));
+ break;
+
+ case 'batch_5':
+ for ($i = 1; $i <= 10; $i++) {
+ $stack[] = "op 5 id $i";
+ }
+ break;
+
+ case 'chained':
+ $stack[] = 'submit handler 1';
+ $stack[] = 'value = ' . $value;
+ $stack = array_merge($stack, $this->_resultStack('batch_1'));
+ $stack[] = 'submit handler 2';
+ $stack[] = 'value = ' . ($value + 1);
+ $stack = array_merge($stack, $this->_resultStack('batch_2'));
+ $stack[] = 'submit handler 3';
+ $stack[] = 'value = ' . ($value + 2);
+ $stack[] = 'submit handler 4';
+ $stack[] = 'value = ' . ($value + 3);
+ $stack = array_merge($stack, $this->_resultStack('batch_3'));
+ break;
+ }
+ return $stack;
+ }
+
+ /**
+ * Helper function: return expected result messages for the test batches.
+ */
+ function _resultMessages($id) {
+ $messages = array();
+
+ switch ($id) {
+ case 'batch_0':
+ $messages[] = 'results for batch 0<br />none';
+ break;
+
+ case 'batch_1':
+ $messages[] = 'results for batch 1<br />op 1: processed 10 elements';
+ break;
+
+ case 'batch_2':
+ $messages[] = 'results for batch 2<br />op 2: processed 10 elements';
+ break;
+
+ case 'batch_3':
+ $messages[] = 'results for batch 3<br />op 1: processed 10 elements<br />op 2: processed 10 elements';
+ break;
+
+ case 'batch_4':
+ $messages[] = 'results for batch 4<br />op 1: processed 10 elements';
+ $messages = array_merge($messages, $this->_resultMessages('batch_2'));
+ break;
+
+ case 'batch_5':
+ $messages[] = 'results for batch 5<br />op 1: processed 10 elements. $context[\'finished\'] > 1 returned from batch process, with success.';
+ break;
+
+ case 'chained':
+ $messages = array_merge($messages, $this->_resultMessages('batch_1'));
+ $messages = array_merge($messages, $this->_resultMessages('batch_2'));
+ $messages = array_merge($messages, $this->_resultMessages('batch_3'));
+ break;
+ }
+ return $messages;
+ }
+}
+
+/**
+ * Tests for the Batch API Progress page.
+ */
+class BatchPageTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Batch progress page',
+ 'description' => 'Test the content of the progress page.',
+ 'group' => 'Batch API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('batch_test');
+ }
+
+ /**
+ * Tests that the batch API progress page uses the correct theme.
+ */
+ function testBatchProgressPageTheme() {
+ // Make sure that the page which starts the batch (an administrative page)
+ // is using a different theme than would normally be used by the batch API.
+ variable_set('theme_default', 'bartik');
+ variable_set('admin_theme', 'seven');
+ // Log in as an administrator who can see the administrative theme.
+ $admin_user = $this->drupalCreateUser(array('view the administration theme'));
+ $this->drupalLogin($admin_user);
+ // Visit an administrative page that runs a test batch, and check that the
+ // theme that was used during batch execution (which the batch callback
+ // function saved as a variable) matches the theme used on the
+ // administrative page.
+ $this->drupalGet('admin/batch-test/test-theme');
+ // The stack should contain the name of the theme used on the progress
+ // page.
+ $this->assertEqual(batch_test_stack(), array('seven'), t('A progressive batch correctly uses the theme of the page that started the batch.'));
+ }
+}
+
+/**
+ * Tests the function _batch_api_percentage() to make sure that the rounding
+ * works properly in all cases.
+ */
+class BatchPercentagesUnitTestCase extends DrupalUnitTestCase {
+ protected $testCases = array();
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Batch percentages',
+ 'description' => 'Unit tests of progress percentage rounding.',
+ 'group' => 'Batch API',
+ );
+ }
+
+ function setUp() {
+ // Set up an array of test cases, where the expected values are the keys,
+ // and the values are arrays with the keys 'total' and 'current',
+ // corresponding with the function parameters of _batch_api_percentage().
+ $this->testCases = array(
+ // 1/2 is 50%.
+ '50' => array('total' => 2, 'current' => 1),
+ // Though we should never encounter a case where the current set is set
+ // 0, if we did, we should get 0%.
+ '0' => array('total' => 3, 'current' => 0),
+ // 1/3 is closer to 33% than to 34%.
+ '33' => array('total' => 3, 'current' => 1),
+ // 2/3 is closer to 67% than to 66%.
+ '67' => array('total' => 3, 'current' => 2),
+ // 1/199 should round up to 1%.
+ '1' => array('total' => 199, 'current' => 1),
+ // 198/199 should round down to 99%.
+ '99' => array('total' => 199, 'current' => 198),
+ // 199/200 would have rounded up to 100%, which would give the false
+ // impression of being finished, so we add another digit and should get
+ // 99.5%.
+ '99.5' => array('total' => 200, 'current' => 199),
+ // The same logic holds for 1/200: we should get 0.5%.
+ '0.5' => array('total' => 200, 'current' => 1),
+ // Numbers that come out evenly, such as 50/200, should be forced to have
+ // extra digits for consistancy.
+ '25.0' => array('total' => 200, 'current' => 50),
+ // Regardless of number of digits we're using, 100% should always just be
+ // 100%.
+ '100' => array('total' => 200, 'current' => 200),
+ // 1998/1999 should similarly round down to 99.9%.
+ '99.9' => array('total' => 1999, 'current' => 1998),
+ // 1999/2000 should add another digit and go to 99.95%.
+ '99.95' => array('total' => 2000, 'current' => 1999),
+ // 19999/20000 should add yet another digit and go to 99.995%.
+ '99.995' => array('total' => 20000, 'current' => 19999),
+ // The next five test cases simulate a batch with a single operation
+ // ('total' equals 1) that takes several steps to complete. Within the
+ // operation, we imagine that there are 501 items to process, and 100 are
+ // completed during each step. The percentages we get back should be
+ // rounded the usual way for the first few passes (i.e., 20%, 40%, etc.),
+ // but for the last pass through, when 500 out of 501 items have been
+ // processed, we do not want to round up to 100%, since that would
+ // erroneously indicate that the processing is complete.
+ '20' => array('total' => 1, 'current' => 100/501),
+ '40' => array('total' => 1, 'current' => 200/501),
+ '60' => array('total' => 1, 'current' => 300/501),
+ '80' => array('total' => 1, 'current' => 400/501),
+ '99.8' => array('total' => 1, 'current' => 500/501),
+ );
+ require_once DRUPAL_ROOT . '/includes/batch.inc';
+ parent::setUp();
+ }
+
+ /**
+ * Test the _batch_api_percentage() function.
+ */
+ function testBatchPercentages() {
+ foreach ($this->testCases as $expected_result => $arguments) {
+ // PHP sometimes casts numeric strings that are array keys to integers,
+ // cast them back here.
+ $expected_result = (string) $expected_result;
+ $total = $arguments['total'];
+ $current = $arguments['current'];
+ $actual_result = _batch_api_percentage($total, $current);
+ if ($actual_result === $expected_result) {
+ $this->pass(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, and got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
+ }
+ else {
+ $this->fail(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, but got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
+ }
+ }
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.callbacks.inc b/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.callbacks.inc
new file mode 100644
index 0000000..75e6655
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.callbacks.inc
@@ -0,0 +1,141 @@
+<?php
+
+
+/**
+ * @file
+ * Batch callbacks for the Batch API tests.
+ */
+
+/**
+ * Simple batch operation.
+ */
+function _batch_test_callback_1($id, $sleep, &$context) {
+ // No-op, but ensure the batch take a couple iterations.
+ // Batch needs time to run for the test, so sleep a bit.
+ usleep($sleep);
+ // Track execution, and store some result for post-processing in the
+ // 'finished' callback.
+ batch_test_stack("op 1 id $id");
+ $context['results'][1][] = $id;
+}
+
+/**
+ * Multistep batch operation.
+ */
+function _batch_test_callback_2($start, $total, $sleep, &$context) {
+ // Initialize context with progress information.
+ if (!isset($context['sandbox']['current'])) {
+ $context['sandbox']['current'] = $start;
+ $context['sandbox']['count'] = 0;
+ }
+
+ // Process by groups of 5 (arbitrary value).
+ $limit = 5;
+ for ($i = 0; $i < $limit && $context['sandbox']['count'] < $total; $i++) {
+ // No-op, but ensure the batch take a couple iterations.
+ // Batch needs time to run for the test, so sleep a bit.
+ usleep($sleep);
+ // Track execution, and store some result for post-processing in the
+ // 'finished' callback.
+ $id = $context['sandbox']['current'] + $i;
+ batch_test_stack("op 2 id $id");
+ $context['results'][2][] = $id;
+
+ // Update progress information.
+ $context['sandbox']['count']++;
+ }
+ $context['sandbox']['current'] += $i;
+
+ // Inform batch engine about progress.
+ if ($context['sandbox']['count'] != $total) {
+ $context['finished'] = $context['sandbox']['count'] / $total;
+ }
+}
+
+/**
+ * Simple batch operation.
+ */
+function _batch_test_callback_5($id, $sleep, &$context) {
+ // No-op, but ensure the batch take a couple iterations.
+ // Batch needs time to run for the test, so sleep a bit.
+ usleep($sleep);
+ // Track execution, and store some result for post-processing in the
+ // 'finished' callback.
+ batch_test_stack("op 5 id $id");
+ $context['results'][5][] = $id;
+ // This test is to test finished > 1
+ $context['finished'] = 3.14;
+}
+
+/**
+ * Batch operation setting up its own batch.
+ */
+function _batch_test_nested_batch_callback() {
+ batch_test_stack('setting up batch 2');
+ batch_set(_batch_test_batch_2());
+}
+
+/**
+ * Common 'finished' callbacks for batches 1 to 4.
+ */
+function _batch_test_finished_helper($batch_id, $success, $results, $operations) {
+ $messages = array("results for batch $batch_id");
+ if ($results) {
+ foreach ($results as $op => $op_results) {
+ $messages[] = 'op '. $op . ': processed ' . count($op_results) . ' elements';
+ }
+ }
+ else {
+ $messages[] = 'none';
+ }
+
+ if (!$success) {
+ // A fatal error occurred during the processing.
+ $error_operation = reset($operations);
+ $messages[] = t('An error occurred while processing @op with arguments:<br/>@args', array('@op' => $error_operation[0], '@args' => print_r($error_operation[1], TRUE)));
+ }
+
+ drupal_set_message(implode('<br />', $messages));
+}
+
+/**
+ * 'finished' callback for batch 0.
+ */
+function _batch_test_finished_0($success, $results, $operations) {
+ _batch_test_finished_helper(0, $success, $results, $operations);
+}
+
+/**
+ * 'finished' callback for batch 1.
+ */
+function _batch_test_finished_1($success, $results, $operations) {
+ _batch_test_finished_helper(1, $success, $results, $operations);
+}
+
+/**
+ * 'finished' callback for batch 2.
+ */
+function _batch_test_finished_2($success, $results, $operations) {
+ _batch_test_finished_helper(2, $success, $results, $operations);
+}
+
+/**
+ * 'finished' callback for batch 3.
+ */
+function _batch_test_finished_3($success, $results, $operations) {
+ _batch_test_finished_helper(3, $success, $results, $operations);
+}
+
+/**
+ * 'finished' callback for batch 4.
+ */
+function _batch_test_finished_4($success, $results, $operations) {
+ _batch_test_finished_helper(4, $success, $results, $operations);
+}
+
+/**
+ * 'finished' callback for batch 5.
+ */
+function _batch_test_finished_5($success, $results, $operations) {
+ _batch_test_finished_helper(5, $success, $results, $operations);
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.info
new file mode 100644
index 0000000..c69cd11
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.info
@@ -0,0 +1,12 @@
+name = "Batch API test"
+description = "Support module for Batch API tests."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.module
new file mode 100644
index 0000000..1200e76
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/batch_test.module
@@ -0,0 +1,513 @@
+<?php
+
+/**
+ * @file
+ * Helper module for the Batch API tests.
+ */
+
+/**
+ * Implement hook_menu().
+ */
+function batch_test_menu() {
+ $items = array();
+
+ $items['batch-test'] = array(
+ 'title' => 'Batch test',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('batch_test_simple_form'),
+ 'access callback' => TRUE,
+ );
+ // Simple form: one submit handler, setting a batch.
+ $items['batch-test/simple'] = array(
+ 'title' => 'Simple',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ 'weight' => 0,
+ );
+ // Multistep form: two steps, each setting a batch.
+ $items['batch-test/multistep'] = array(
+ 'title' => 'Multistep',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('batch_test_multistep_form'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 1,
+ );
+ // Chained form: four submit handlers, several of which set a batch.
+ $items['batch-test/chained'] = array(
+ 'title' => 'Chained',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('batch_test_chained_form'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 2,
+ );
+ // Programmatic form: the page submits the 'Chained' form through
+ // drupal_form_submit().
+ $items['batch-test/programmatic'] = array(
+ 'title' => 'Programmatic',
+ 'page callback' => 'batch_test_programmatic',
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 3,
+ );
+ // No form: fire a batch simply by accessing a page.
+ $items['batch-test/no-form'] = array(
+ 'title' => 'Simple page',
+ 'page callback' => 'batch_test_no_form',
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 4,
+ );
+ // No form: fire a batch; return > 100% complete
+ $items['batch-test/large-percentage'] = array(
+ 'title' => 'Simple page with batch over 100% complete',
+ 'page callback' => 'batch_test_large_percentage',
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 5,
+ );
+ // Tests programmatic form submission within a batch operation.
+ $items['batch-test/nested-programmatic'] = array(
+ 'title' => 'Nested programmatic',
+ 'page callback' => 'batch_test_nested_drupal_form_submit',
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 6,
+ );
+ // Landing page to test redirects.
+ $items['batch-test/redirect'] = array(
+ 'title' => 'Redirect',
+ 'page callback' => 'batch_test_redirect_page',
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 7,
+ );
+ // This item lives under 'admin' so that the page uses the admin theme.
+ $items['admin/batch-test/test-theme'] = array(
+ 'page callback' => 'batch_test_theme_batch',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
+ return $items;
+}
+
+/**
+ * Simple form.
+ */
+function batch_test_simple_form() {
+ $form['batch'] = array(
+ '#type' => 'select',
+ '#title' => 'Choose batch',
+ '#options' => array(
+ 'batch_0' => 'batch 0',
+ 'batch_1' => 'batch 1',
+ 'batch_2' => 'batch 2',
+ 'batch_3' => 'batch 3',
+ 'batch_4' => 'batch 4',
+ ),
+ );
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => 'Submit',
+ );
+
+ return $form;
+}
+
+/**
+ * Submit handler for the simple form.
+ */
+function batch_test_simple_form_submit($form, &$form_state) {
+ batch_test_stack(NULL, TRUE);
+
+ $function = '_batch_test_' . $form_state['values']['batch'];
+ batch_set($function());
+
+ $form_state['redirect'] = 'batch-test/redirect';
+}
+
+
+/**
+ * Multistep form.
+ */
+function batch_test_multistep_form($form, &$form_state) {
+ if (empty($form_state['storage']['step'])) {
+ $form_state['storage']['step'] = 1;
+ }
+
+ $form['step_display'] = array(
+ '#markup' => 'step ' . $form_state['storage']['step'] . '<br/>',
+ );
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => 'Submit',
+ );
+
+ return $form;
+}
+
+/**
+ * Submit handler for the multistep form.
+ */
+function batch_test_multistep_form_submit($form, &$form_state) {
+ batch_test_stack(NULL, TRUE);
+
+ switch ($form_state['storage']['step']) {
+ case 1:
+ batch_set(_batch_test_batch_1());
+ break;
+ case 2:
+ batch_set(_batch_test_batch_2());
+ break;
+ }
+
+ if ($form_state['storage']['step'] < 2) {
+ $form_state['storage']['step']++;
+ $form_state['rebuild'] = TRUE;
+ }
+
+ // This will only be effective on the last step.
+ $form_state['redirect'] = 'batch-test/redirect';
+}
+
+/**
+ * Form with chained submit callbacks.
+ */
+function batch_test_chained_form() {
+ // This value is used to test that $form_state persists through batched
+ // submit handlers.
+ $form['value'] = array(
+ '#type' => 'textfield',
+ '#title' => 'Value',
+ '#default_value' => 1,
+ );
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => 'Submit',
+ );
+ $form['#submit'] = array(
+ 'batch_test_chained_form_submit_1',
+ 'batch_test_chained_form_submit_2',
+ 'batch_test_chained_form_submit_3',
+ 'batch_test_chained_form_submit_4',
+ );
+
+ return $form;
+}
+
+/**
+ * Submit handler #1 for the chained form.
+ */
+function batch_test_chained_form_submit_1($form, &$form_state) {
+ batch_test_stack(NULL, TRUE);
+
+ batch_test_stack('submit handler 1');
+ batch_test_stack('value = ' . $form_state['values']['value']);
+
+ $form_state['values']['value']++;
+ batch_set(_batch_test_batch_1());
+
+ // This redirect should not be taken into account.
+ $form_state['redirect'] = 'should/be/discarded';
+}
+
+/**
+ * Submit handler #2 for the chained form.
+ */
+function batch_test_chained_form_submit_2($form, &$form_state) {
+ batch_test_stack('submit handler 2');
+ batch_test_stack('value = ' . $form_state['values']['value']);
+
+ $form_state['values']['value']++;
+ batch_set(_batch_test_batch_2());
+
+ // This redirect should not be taken into account.
+ $form_state['redirect'] = 'should/be/discarded';
+}
+
+/**
+ * Submit handler #3 for the chained form.
+ */
+function batch_test_chained_form_submit_3($form, &$form_state) {
+ batch_test_stack('submit handler 3');
+ batch_test_stack('value = ' . $form_state['values']['value']);
+
+ $form_state['values']['value']++;
+
+ // This redirect should not be taken into account.
+ $form_state['redirect'] = 'should/be/discarded';
+}
+
+/**
+ * Submit handler #4 for the chained form.
+ */
+function batch_test_chained_form_submit_4($form, &$form_state) {
+ batch_test_stack('submit handler 4');
+ batch_test_stack('value = ' . $form_state['values']['value']);
+
+ $form_state['values']['value']++;
+ batch_set(_batch_test_batch_3());
+
+ // This is the redirect that should prevail.
+ $form_state['redirect'] = 'batch-test/redirect';
+}
+
+/**
+ * Menu callback: programmatically submits the 'Chained' form.
+ */
+function batch_test_programmatic($value = 1) {
+ $form_state = array(
+ 'values' => array('value' => $value)
+ );
+ drupal_form_submit('batch_test_chained_form', $form_state);
+ return 'Got out of a programmatic batched form.';
+}
+
+/**
+ * Menu callback: programmatically submits a form within a batch.
+ */
+function batch_test_nested_drupal_form_submit($value = 1) {
+ // Set the batch and process it.
+ $batch['operations'] = array(
+ array('_batch_test_nested_drupal_form_submit_callback', array($value)),
+ );
+ batch_set($batch);
+ batch_process('batch-test/redirect');
+}
+
+/**
+ * Batch operation: submits form_test_mock_form using drupal_form_submit().
+ */
+function _batch_test_nested_drupal_form_submit_callback($value) {
+ $state['values']['test_value'] = $value;
+ drupal_form_submit('batch_test_mock_form', $state);
+}
+
+/**
+ * A simple form with a textfield and submit button.
+ */
+function batch_test_mock_form($form, $form_state) {
+ $form['test_value'] = array(
+ '#type' => 'textfield',
+ );
+ $form['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Submit'),
+ );
+
+ return $form;
+}
+
+/**
+ * Submit handler for the batch_test_mock form.
+ */
+function batch_test_mock_form_submit($form, &$form_state) {
+ batch_test_stack('mock form submitted with value = ' . $form_state['values']['test_value']);
+}
+
+/**
+ * Menu callback: fire a batch process without a form submission.
+ */
+function batch_test_no_form() {
+ batch_test_stack(NULL, TRUE);
+
+ batch_set(_batch_test_batch_1());
+ batch_process('batch-test/redirect');
+}
+
+/**
+ * Menu callback: fire a batch process without a form submission.
+ */
+function batch_test_large_percentage() {
+ batch_test_stack(NULL, TRUE);
+
+ batch_set(_batch_test_batch_5());
+ batch_process('batch-test/redirect');
+}
+
+/**
+ * Menu callback: successful redirection.
+ */
+function batch_test_redirect_page() {
+ return 'Redirection successful.';
+}
+
+/**
+ * Batch 0: no operation.
+ */
+function _batch_test_batch_0() {
+ $batch = array(
+ 'operations' => array(),
+ 'finished' => '_batch_test_finished_0',
+ 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
+ );
+ return $batch;
+}
+
+/**
+ * Batch 1: repeats a simple operation.
+ *
+ * Operations: op 1 from 1 to 10.
+ */
+function _batch_test_batch_1() {
+ // Ensure the batch takes at least two iterations.
+ $total = 10;
+ $sleep = (1000000 / $total) * 2;
+
+ $operations = array();
+ for ($i = 1; $i <= $total; $i++) {
+ $operations[] = array('_batch_test_callback_1', array($i, $sleep));
+ }
+ $batch = array(
+ 'operations' => $operations,
+ 'finished' => '_batch_test_finished_1',
+ 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
+ );
+ return $batch;
+}
+
+/**
+ * Batch 2: single multistep operation.
+ *
+ * Operations: op 2 from 1 to 10.
+ */
+function _batch_test_batch_2() {
+ // Ensure the batch takes at least two iterations.
+ $total = 10;
+ $sleep = (1000000 / $total) * 2;
+
+ $operations = array(
+ array('_batch_test_callback_2', array(1, $total, $sleep)),
+ );
+ $batch = array(
+ 'operations' => $operations,
+ 'finished' => '_batch_test_finished_2',
+ 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
+ );
+ return $batch;
+}
+
+/**
+ * Batch 3: both single and multistep operations.
+ *
+ * Operations:
+ * - op 1 from 1 to 5,
+ * - op 2 from 1 to 5,
+ * - op 1 from 6 to 10,
+ * - op 2 from 6 to 10.
+ */
+function _batch_test_batch_3() {
+ // Ensure the batch takes at least two iterations.
+ $total = 10;
+ $sleep = (1000000 / $total) * 2;
+
+ $operations = array();
+ for ($i = 1; $i <= round($total / 2); $i++) {
+ $operations[] = array('_batch_test_callback_1', array($i, $sleep));
+ }
+ $operations[] = array('_batch_test_callback_2', array(1, $total / 2, $sleep));
+ for ($i = round($total / 2) + 1; $i <= $total; $i++) {
+ $operations[] = array('_batch_test_callback_1', array($i, $sleep));
+ }
+ $operations[] = array('_batch_test_callback_2', array(6, $total / 2, $sleep));
+ $batch = array(
+ 'operations' => $operations,
+ 'finished' => '_batch_test_finished_3',
+ 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
+ );
+ return $batch;
+}
+
+/**
+ * Batch 4: batch within a batch.
+ *
+ * Operations:
+ * - op 1 from 1 to 5,
+ * - set batch 2 (op 2 from 1 to 10, should run at the end)
+ * - op 1 from 6 to 10,
+ */
+function _batch_test_batch_4() {
+ // Ensure the batch takes at least two iterations.
+ $total = 10;
+ $sleep = (1000000 / $total) * 2;
+
+ $operations = array();
+ for ($i = 1; $i <= round($total / 2); $i++) {
+ $operations[] = array('_batch_test_callback_1', array($i, $sleep));
+ }
+ $operations[] = array('_batch_test_nested_batch_callback', array());
+ for ($i = round($total / 2) + 1; $i <= $total; $i++) {
+ $operations[] = array('_batch_test_callback_1', array($i, $sleep));
+ }
+ $batch = array(
+ 'operations' => $operations,
+ 'finished' => '_batch_test_finished_4',
+ 'file' => drupal_get_path('module', 'batch_test') . '/batch_test.callbacks.inc',
+ );
+ return $batch;
+}
+
+/**
+ * Batch 5: repeats a simple operation.
+ *
+ * Operations: op 1 from 1 to 10.
+ */
+function _batch_test_batch_5() {
+ // Ensure the batch takes at least two iterations.
+ $total = 10;
+ $sleep = (1000000 / $total) * 2;
+
+ $operations = array();
+ for ($i = 1; $i <= $total; $i++) {
+ $operations[] = array('_batch_test_callback_5', array($i, $sleep));
+ }
+ $batch = array(
+ 'operations' => $operations,
+ 'finished' => '_batch_test_finished_5',
+ 'file' => drupal_get_path('module', 'batch_test'). '/batch_test.callbacks.inc',
+ );
+ return $batch;
+}
+
+/**
+ * Menu callback: run a batch for testing theme used on the progress page.
+ */
+function batch_test_theme_batch() {
+ batch_test_stack(NULL, TRUE);
+ $batch = array(
+ 'operations' => array(
+ array('_batch_test_theme_callback', array()),
+ ),
+ );
+ batch_set($batch);
+ batch_process('batch-test/redirect');
+}
+
+/**
+ * Batch callback function for testing the theme used on the progress page.
+ */
+function _batch_test_theme_callback() {
+ // Because drupalGet() steps through the full progressive batch before
+ // returning control to the test function, we cannot test that the correct
+ // theme is being used on the batch processing page by viewing that page
+ // directly. Instead, we save the theme being used in a variable here, so
+ // that it can be loaded and inspected in the thread running the test.
+ global $theme;
+ batch_test_stack($theme);
+}
+
+/**
+ * Helper function: store or retrieve traced execution data.
+ */
+function batch_test_stack($data = NULL, $reset = FALSE) {
+ if ($reset) {
+ variable_del('batch_test_stack');
+ }
+ if (!isset($data)) {
+ return variable_get('batch_test_stack', array());
+ }
+ $stack = variable_get('batch_test_stack', array());
+ $stack[] = $data;
+ variable_set('batch_test_stack', $stack);
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/bootstrap.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/bootstrap.test
new file mode 100644
index 0000000..4fda15c
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/bootstrap.test
@@ -0,0 +1,543 @@
+<?php
+
+class BootstrapIPAddressTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'IP address and HTTP_HOST test',
+ 'description' => 'Get the IP address from the current visitor from the server variables, check hostname validation.',
+ 'group' => 'Bootstrap'
+ );
+ }
+
+ function setUp() {
+ $this->oldserver = $_SERVER;
+
+ $this->remote_ip = '127.0.0.1';
+ $this->proxy_ip = '127.0.0.2';
+ $this->proxy2_ip = '127.0.0.3';
+ $this->forwarded_ip = '127.0.0.4';
+ $this->cluster_ip = '127.0.0.5';
+ $this->untrusted_ip = '0.0.0.0';
+
+ drupal_static_reset('ip_address');
+
+ $_SERVER['REMOTE_ADDR'] = $this->remote_ip;
+ unset($_SERVER['HTTP_X_FORWARDED_FOR']);
+ unset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']);
+
+ parent::setUp();
+ }
+
+ function tearDown() {
+ $_SERVER = $this->oldserver;
+ drupal_static_reset('ip_address');
+ parent::tearDown();
+ }
+
+ /**
+ * test IP Address and hostname
+ */
+ function testIPAddressHost() {
+ // Test the normal IP address.
+ $this->assertTrue(
+ ip_address() == $this->remote_ip,
+ 'Got remote IP address.'
+ );
+
+ // Proxy forwarding on but no proxy addresses defined.
+ variable_set('reverse_proxy', 1);
+ $this->assertTrue(
+ ip_address() == $this->remote_ip,
+ 'Proxy forwarding without trusted proxies got remote IP address.'
+ );
+
+ // Proxy forwarding on and proxy address not trusted.
+ variable_set('reverse_proxy_addresses', array($this->proxy_ip, $this->proxy2_ip));
+ drupal_static_reset('ip_address');
+ $_SERVER['REMOTE_ADDR'] = $this->untrusted_ip;
+ $this->assertTrue(
+ ip_address() == $this->untrusted_ip,
+ 'Proxy forwarding with untrusted proxy got remote IP address.'
+ );
+
+ // Proxy forwarding on and proxy address trusted.
+ $_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
+ $_SERVER['HTTP_X_FORWARDED_FOR'] = $this->forwarded_ip;
+ drupal_static_reset('ip_address');
+ $this->assertTrue(
+ ip_address() == $this->forwarded_ip,
+ 'Proxy forwarding with trusted proxy got forwarded IP address.'
+ );
+
+ // Multi-tier architecture with comma separated values in header.
+ $_SERVER['REMOTE_ADDR'] = $this->proxy_ip;
+ $_SERVER['HTTP_X_FORWARDED_FOR'] = implode(', ', array($this->untrusted_ip, $this->forwarded_ip, $this->proxy2_ip));
+ drupal_static_reset('ip_address');
+ $this->assertTrue(
+ ip_address() == $this->forwarded_ip,
+ 'Proxy forwarding with trusted 2-tier proxy got forwarded IP address.'
+ );
+
+ // Custom client-IP header.
+ variable_set('reverse_proxy_header', 'HTTP_X_CLUSTER_CLIENT_IP');
+ $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] = $this->cluster_ip;
+ drupal_static_reset('ip_address');
+ $this->assertTrue(
+ ip_address() == $this->cluster_ip,
+ 'Cluster environment got cluster client IP.'
+ );
+
+ // Verifies that drupal_valid_http_host() prevents invalid characters.
+ $this->assertFalse(drupal_valid_http_host('security/.drupal.org:80'), 'HTTP_HOST with / is invalid');
+ $this->assertFalse(drupal_valid_http_host('security\\.drupal.org:80'), 'HTTP_HOST with \\ is invalid');
+ $this->assertFalse(drupal_valid_http_host('security<.drupal.org:80'), 'HTTP_HOST with &lt; is invalid');
+ $this->assertFalse(drupal_valid_http_host('security..drupal.org:80'), 'HTTP_HOST with .. is invalid');
+ // IPv6 loopback address
+ $this->assertTrue(drupal_valid_http_host('[::1]:80'), 'HTTP_HOST containing IPv6 loopback is valid');
+ }
+}
+
+class BootstrapPageCacheTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Page cache test',
+ 'description' => 'Enable the page cache and test it with various HTTP requests.',
+ 'group' => 'Bootstrap'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('system_test');
+ }
+
+ /**
+ * Test support for requests containing If-Modified-Since and If-None-Match headers.
+ */
+ function testConditionalRequests() {
+ variable_set('cache', 1);
+
+ // Fill the cache.
+ $this->drupalGet('');
+
+ $this->drupalHead('');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+ $etag = $this->drupalGetHeader('ETag');
+ $last_modified = $this->drupalGetHeader('Last-Modified');
+
+ $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
+ $this->assertResponse(304, 'Conditional request returned 304 Not Modified.');
+
+ $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag));
+ $this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
+
+ $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag));
+ $this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
+
+ $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified));
+ $this->assertResponse(200, 'Conditional request without If-None-Match returned 200 OK.');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+
+ $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC1123, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
+ $this->assertResponse(200, 'Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+
+ $user = $this->drupalCreateUser();
+ $this->drupalLogin($user);
+ $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
+ $this->assertResponse(200, 'Conditional request returned 200 OK for authenticated user.');
+ $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absense of Page was not cached.');
+ }
+
+ /**
+ * Test cache headers.
+ */
+ function testPageCache() {
+ variable_set('cache', 1);
+
+ // Fill the cache.
+ $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+ $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', 'Vary header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', 'Cache-Control header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
+
+ // Check cache.
+ $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+ $this->assertEqual($this->drupalGetHeader('Vary'), 'Cookie,Accept-Encoding', 'Vary: Cookie header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'public, max-age=0', 'Cache-Control header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
+
+ // Check replacing default headers.
+ $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
+ $this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', 'Default header was replaced.');
+ $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
+ $this->assertEqual($this->drupalGetHeader('Vary'), 'User-Agent,Accept-Encoding', 'Default header was replaced.');
+
+ // Check that authenticated users bypass the cache.
+ $user = $this->drupalCreateUser();
+ $this->drupalLogin($user);
+ $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+ $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.');
+ $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
+ $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', 'Cache-Control header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
+ $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
+
+ }
+
+ /**
+ * Test page compression.
+ *
+ * The test should pass even if zlib.output_compression is enabled in php.ini,
+ * .htaccess or similar, or if compression is done outside PHP, e.g. by the
+ * mod_deflate Apache module.
+ */
+ function testPageCompression() {
+ variable_set('cache', 1);
+
+ // Fill the cache and verify that output is compressed.
+ $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+ $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
+ $this->assertRaw('</html>', 'Page was gzip compressed.');
+
+ // Verify that cached output is compressed.
+ $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+ $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', 'A Content-Encoding header was sent.');
+ $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
+ $this->assertRaw('</html>', 'Page was gzip compressed.');
+
+ // Verify that a client without compression support gets an uncompressed page.
+ $this->drupalGet('');
+ $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+ $this->assertFalse($this->drupalGetHeader('Content-Encoding'), 'A Content-Encoding header was not sent.');
+ $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), 'Site title matches.');
+ $this->assertRaw('</html>', 'Page was not compressed.');
+
+ // Disable compression mode.
+ variable_set('page_compression', FALSE);
+
+ // Verify if cached page is still available for a client with compression support.
+ $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
+ $this->drupalSetContent(gzinflate(substr($this->drupalGetContent(), 10, -8)));
+ $this->assertRaw('</html>', 'Page was delivered after compression mode is changed (compression support enabled).');
+
+ // Verify if cached page is still available for a client without compression support.
+ $this->drupalGet('');
+ $this->assertRaw('</html>', 'Page was delivered after compression mode is changed (compression support disabled).');
+ }
+}
+
+class BootstrapVariableTestCase extends DrupalWebTestCase {
+
+ function setUp() {
+ parent::setUp('system_test');
+ }
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Variable test',
+ 'description' => 'Make sure the variable system functions correctly.',
+ 'group' => 'Bootstrap'
+ );
+ }
+
+ /**
+ * testVariable
+ */
+ function testVariable() {
+ // Setting and retrieving values.
+ $variable = $this->randomName();
+ variable_set('simpletest_bootstrap_variable_test', $variable);
+ $this->assertIdentical($variable, variable_get('simpletest_bootstrap_variable_test'), 'Setting and retrieving values');
+
+ // Make sure the variable persists across multiple requests.
+ $this->drupalGet('system-test/variable-get');
+ $this->assertText($variable, 'Variable persists across multiple requests');
+
+ // Deleting variables.
+ $default_value = $this->randomName();
+ variable_del('simpletest_bootstrap_variable_test');
+ $variable = variable_get('simpletest_bootstrap_variable_test', $default_value);
+ $this->assertIdentical($variable, $default_value, 'Deleting variables');
+ }
+
+ /**
+ * Makes sure that the default variable parameter is passed through okay.
+ */
+ function testVariableDefaults() {
+ // Tests passing nothing through to the default.
+ $this->assertIdentical(NULL, variable_get('simpletest_bootstrap_variable_test'), 'Variables are correctly defaulting to NULL.');
+
+ // Tests passing 5 to the default parameter.
+ $this->assertIdentical(5, variable_get('simpletest_bootstrap_variable_test', 5), 'The default variable parameter is passed through correctly.');
+ }
+
+}
+
+/**
+ * Test hook_boot() and hook_exit().
+ */
+class HookBootExitTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Boot and exit hook invocation',
+ 'description' => 'Test that hook_boot() and hook_exit() are called correctly.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('system_test', 'dblog');
+ }
+
+ /**
+ * Test calling of hook_boot() and hook_exit().
+ */
+ function testHookBootExit() {
+ // Test with cache disabled. Boot and exit should always fire.
+ variable_set('cache', 0);
+ $this->drupalGet('');
+ $calls = 1;
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with disabled cache.'));
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with disabled cache.'));
+
+ // Test with normal cache. Boot and exit should be called.
+ variable_set('cache', 1);
+ $this->drupalGet('');
+ $calls++;
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with normal cache.'));
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with normal cache.'));
+
+ // Boot and exit should not fire since the page is cached.
+ variable_set('page_cache_invoke_hooks', FALSE);
+ $this->assertTrue(cache_get(url('', array('absolute' => TRUE)), 'cache_page'), t('Page has been cached.'));
+ $this->drupalGet('');
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot not called with aggressive cache and a cached page.'));
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit not called with aggressive cache and a cached page.'));
+
+ // Test with page cache cleared, boot and exit should be called.
+ $this->assertTrue(db_delete('cache_page')->execute(), t('Page cache cleared.'));
+ $this->drupalGet('');
+ $calls++;
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with aggressive cache and no cached page.'));
+ $this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with aggressive cache and no cached page.'));
+ }
+}
+
+/**
+ * Test drupal_get_filename()'s availability.
+ */
+class BootstrapGetFilenameTestCase extends DrupalUnitTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Get filename test',
+ 'description' => 'Test that drupal_get_filename() works correctly when the file is not found in the database.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ /**
+ * Test that drupal_get_filename() works correctly when the file is not found in the database.
+ */
+ function testDrupalGetFilename() {
+ // Reset the static cache so we can test the "db is not active" code of
+ // drupal_get_filename().
+ drupal_static_reset('drupal_get_filename');
+
+ // Retrieving the location of a module.
+ $this->assertIdentical(drupal_get_filename('module', 'php'), 'modules/php/php.module', t('Retrieve module location.'));
+
+ // Retrieving the location of a theme.
+ $this->assertIdentical(drupal_get_filename('theme', 'stark'), 'themes/stark/stark.info', t('Retrieve theme location.'));
+
+ // Retrieving the location of a theme engine.
+ $this->assertIdentical(drupal_get_filename('theme_engine', 'phptemplate'), 'themes/engines/phptemplate/phptemplate.engine', t('Retrieve theme engine location.'));
+
+ // Retrieving the location of a profile. Profiles are a special case with
+ // a fixed location and naming.
+ $this->assertIdentical(drupal_get_filename('profile', 'standard'), 'profiles/standard/standard.profile', t('Retrieve install profile location.'));
+
+ // When a file is not found in the database cache, drupal_get_filename()
+ // searches several locations on the filesystem, including the DRUPAL_ROOT
+ // directory. We use the '.script' extension below because this is a
+ // non-existent filetype that will definitely not exist in the database.
+ // Since there is already a scripts directory, drupal_get_filename() will
+ // automatically check there for 'script' files, just as it does for (e.g.)
+ // 'module' files in modules.
+ $this->assertIdentical(drupal_get_filename('script', 'test'), 'scripts/test.script', t('Retrieve test script location.'));
+ }
+}
+
+class BootstrapTimerTestCase extends DrupalUnitTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Timer test',
+ 'description' => 'Test that timer_read() works both when a timer is running and when a timer is stopped.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ /**
+ * Test timer_read() to ensure it properly accumulates time when the timer
+ * started and stopped multiple times.
+ * @return
+ */
+ function testTimer() {
+ timer_start('test');
+ sleep(1);
+ $this->assertTrue(timer_read('test') >= 1000, 'Timer measured 1 second of sleeping while running.');
+ sleep(1);
+ timer_stop('test');
+ $this->assertTrue(timer_read('test') >= 2000, 'Timer measured 2 seconds of sleeping after being stopped.');
+ timer_start('test');
+ sleep(1);
+ $this->assertTrue(timer_read('test') >= 3000, 'Timer measured 3 seconds of sleeping after being restarted.');
+ sleep(1);
+ $timer = timer_stop('test');
+ $this->assertTrue(timer_read('test') >= 4000, 'Timer measured 4 seconds of sleeping after being stopped for a second time.');
+ $this->assertEqual($timer['count'], 2, 'Timer counted 2 instances of being started.');
+ }
+}
+
+/**
+ * Test that resetting static variables works.
+ */
+class BootstrapResettableStaticTestCase extends DrupalUnitTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Resettable static variables test',
+ 'description' => 'Test that drupal_static() and drupal_static_reset() work.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ /**
+ * Test that a variable reference returned by drupal_static() gets reset when
+ * drupal_static_reset() is called.
+ */
+ function testDrupalStatic() {
+ $name = __CLASS__ . '_' . __METHOD__;
+ $var = &drupal_static($name, 'foo');
+ $this->assertEqual($var, 'foo', 'Variable returned by drupal_static() was set to its default.');
+
+ // Call the specific reset and the global reset each twice to ensure that
+ // multiple resets can be issued without odd side effects.
+ $var = 'bar';
+ drupal_static_reset($name);
+ $this->assertEqual($var, 'foo', 'Variable was reset after first invocation of name-specific reset.');
+ $var = 'bar';
+ drupal_static_reset($name);
+ $this->assertEqual($var, 'foo', 'Variable was reset after second invocation of name-specific reset.');
+ $var = 'bar';
+ drupal_static_reset();
+ $this->assertEqual($var, 'foo', 'Variable was reset after first invocation of global reset.');
+ $var = 'bar';
+ drupal_static_reset();
+ $this->assertEqual($var, 'foo', 'Variable was reset after second invocation of global reset.');
+ }
+}
+
+/**
+ * Test miscellaneous functions in bootstrap.inc.
+ */
+class BootstrapMiscTestCase extends DrupalUnitTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Miscellaneous bootstrap unit tests',
+ 'description' => 'Test miscellaneous functions in bootstrap.inc.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ /**
+ * Test miscellaneous functions in bootstrap.inc.
+ */
+ function testMisc() {
+ // Test drupal_array_merge_deep().
+ $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => 'X', 'class' => array('a', 'b')), 'language' => 'en');
+ $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('c', 'd')), 'html' => TRUE);
+ $expected = array('fragment' => 'y', 'attributes' => array('title' => 'Y', 'class' => array('a', 'b', 'c', 'd')), 'language' => 'en', 'html' => TRUE);
+ $this->assertIdentical(drupal_array_merge_deep($link_options_1, $link_options_2), $expected, 'drupal_array_merge_deep() returned a properly merged array.');
+ }
+
+ /**
+ * Tests that the drupal_check_memory_limit() function works as expected.
+ */
+ function testCheckMemoryLimit() {
+ $memory_limit = ini_get('memory_limit');
+ // Test that a very reasonable amount of memory is available.
+ $this->assertTrue(drupal_check_memory_limit('30MB'), '30MB of memory tested available.');
+
+ // Get the available memory and multiply it by two to make it unreasonably
+ // high.
+ $twice_avail_memory = ($memory_limit * 2) . 'MB';
+
+ // The function should always return true if the memory limit is set to -1.
+ $this->assertTrue(drupal_check_memory_limit($twice_avail_memory, -1), 'drupal_check_memory_limit() returns TRUE when a limit of -1 (none) is supplied');
+
+ // Test that even though we have 30MB of memory available - the function
+ // returns FALSE when given an upper limit for how much memory can be used.
+ $this->assertFalse(drupal_check_memory_limit('30MB', '16MB'), 'drupal_check_memory_limit() returns FALSE with a 16MB upper limit on a 30MB requirement.');
+
+ // Test that an equal amount of memory to the amount requested returns TRUE.
+ $this->assertTrue(drupal_check_memory_limit('30MB', '30MB'), 'drupal_check_memory_limit() returns TRUE when requesting 30MB on a 30MB requirement.');
+ }
+}
+
+/**
+ * Tests for overriding server variables via the API.
+ */
+class BootstrapOverrideServerVariablesTestCase extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Overriding server variables',
+ 'description' => 'Test that drupal_override_server_variables() works correctly.',
+ 'group' => 'Bootstrap',
+ );
+ }
+
+ /**
+ * Test providing a direct URL to to drupal_override_server_variables().
+ */
+ function testDrupalOverrideServerVariablesProvidedURL() {
+ $tests = array(
+ 'http://example.com' => array(
+ 'HTTP_HOST' => 'example.com',
+ 'SCRIPT_NAME' => isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : NULL,
+ ),
+ 'http://example.com/index.php' => array(
+ 'HTTP_HOST' => 'example.com',
+ 'SCRIPT_NAME' => '/index.php',
+ ),
+ 'http://example.com/subdirectory/index.php' => array(
+ 'HTTP_HOST' => 'example.com',
+ 'SCRIPT_NAME' => '/subdirectory/index.php',
+ ),
+ );
+ foreach ($tests as $url => $expected_server_values) {
+ // Remember the original value of $_SERVER, since the function call below
+ // will modify it.
+ $original_server = $_SERVER;
+ // Call drupal_override_server_variables() and ensure that all expected
+ // $_SERVER variables were modified correctly.
+ drupal_override_server_variables(array('url' => $url));
+ foreach ($expected_server_values as $key => $value) {
+ $this->assertIdentical($_SERVER[$key], $value);
+ }
+ // Restore the original value of $_SERVER.
+ $_SERVER = $original_server;
+ }
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/cache.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/cache.test
new file mode 100644
index 0000000..b42de36
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/cache.test
@@ -0,0 +1,437 @@
+<?php
+
+class CacheTestCase extends DrupalWebTestCase {
+ protected $default_bin = 'cache';
+ protected $default_cid = 'test_temporary';
+ protected $default_value = 'CacheTest';
+
+ /**
+ * Check whether or not a cache entry exists.
+ *
+ * @param $cid
+ * The cache id.
+ * @param $var
+ * The variable the cache should contain.
+ * @param $bin
+ * The bin the cache item was stored in.
+ * @return
+ * TRUE on pass, FALSE on fail.
+ */
+ protected function checkCacheExists($cid, $var, $bin = NULL) {
+ if ($bin == NULL) {
+ $bin = $this->default_bin;
+ }
+
+ $cache = cache_get($cid, $bin);
+
+ return isset($cache->data) && $cache->data == $var;
+ }
+
+ /**
+ * Assert or a cache entry exists.
+ *
+ * @param $message
+ * Message to display.
+ * @param $var
+ * The variable the cache should contain.
+ * @param $cid
+ * The cache id.
+ * @param $bin
+ * The bin the cache item was stored in.
+ */
+ protected function assertCacheExists($message, $var = NULL, $cid = NULL, $bin = NULL) {
+ if ($bin == NULL) {
+ $bin = $this->default_bin;
+ }
+ if ($cid == NULL) {
+ $cid = $this->default_cid;
+ }
+ if ($var == NULL) {
+ $var = $this->default_value;
+ }
+
+ $this->assertTrue($this->checkCacheExists($cid, $var, $bin), $message);
+ }
+
+ /**
+ * Assert or a cache entry has been removed.
+ *
+ * @param $message
+ * Message to display.
+ * @param $cid
+ * The cache id.
+ * @param $bin
+ * The bin the cache item was stored in.
+ */
+ function assertCacheRemoved($message, $cid = NULL, $bin = NULL) {
+ if ($bin == NULL) {
+ $bin = $this->default_bin;
+ }
+ if ($cid == NULL) {
+ $cid = $this->default_cid;
+ }
+
+ $cache = cache_get($cid, $bin);
+ $this->assertFalse($cache, $message);
+ }
+
+ /**
+ * Perform the general wipe.
+ * @param $bin
+ * The bin to perform the wipe on.
+ */
+ protected function generalWipe($bin = NULL) {
+ if ($bin == NULL) {
+ $bin = $this->default_bin;
+ }
+
+ cache_clear_all(NULL, $bin);
+ }
+
+ /**
+ * Setup the lifetime settings for caching.
+ *
+ * @param $time
+ * The time in seconds the cache should minimal live.
+ */
+ protected function setupLifetime($time) {
+ variable_set('cache_lifetime', $time);
+ variable_set('cache_flush', 0);
+ }
+}
+
+class CacheSavingCase extends CacheTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Cache saving test',
+ 'description' => 'Check our variables are saved and restored the right way.',
+ 'group' => 'Cache'
+ );
+ }
+
+ /**
+ * Test the saving and restoring of a string.
+ */
+ function testString() {
+ $this->checkVariable($this->randomName(100));
+ }
+
+ /**
+ * Test the saving and restoring of an integer.
+ */
+ function testInteger() {
+ $this->checkVariable(100);
+ }
+
+ /**
+ * Test the saving and restoring of a double.
+ */
+ function testDouble() {
+ $this->checkVariable(1.29);
+ }
+
+ /**
+ * Test the saving and restoring of an array.
+ */
+ function testArray() {
+ $this->checkVariable(array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6')));
+ }
+
+ /**
+ * Test the saving and restoring of an object.
+ */
+ function testObject() {
+ $test_object = new stdClass();
+ $test_object->test1 = $this->randomName(100);
+ $test_object->test2 = 100;
+ $test_object->test3 = array('drupal1', 'drupal2' => 'drupal3', 'drupal4' => array('drupal5', 'drupal6'));
+
+ cache_set('test_object', $test_object, 'cache');
+ $cache = cache_get('test_object', 'cache');
+ $this->assertTrue(isset($cache->data) && $cache->data == $test_object, 'Object is saved and restored properly.');
+ }
+
+ /**
+ * Check or a variable is stored and restored properly.
+ */
+ function checkVariable($var) {
+ cache_set('test_var', $var, 'cache');
+ $cache = cache_get('test_var', 'cache');
+ $this->assertTrue(isset($cache->data) && $cache->data === $var, format_string('@type is saved and restored properly.', array('@type' => ucfirst(gettype($var)))));
+ }
+
+ /**
+ * Test no empty cids are written in cache table.
+ */
+ function testNoEmptyCids() {
+ $this->drupalGet('user/register');
+ $this->assertFalse(cache_get(''), 'No cache entry is written with an empty cid.');
+ }
+}
+
+/**
+ * Test cache_get_multiple().
+ */
+class CacheGetMultipleUnitTest extends CacheTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Fetching multiple cache items',
+ 'description' => 'Confirm that multiple records are fetched correctly.',
+ 'group' => 'Cache',
+ );
+ }
+
+ function setUp() {
+ $this->default_bin = 'cache_page';
+ parent::setUp();
+ }
+
+ /**
+ * Test cache_get_multiple().
+ */
+ function testCacheMultiple() {
+ $item1 = $this->randomName(10);
+ $item2 = $this->randomName(10);
+ cache_set('item1', $item1, $this->default_bin);
+ cache_set('item2', $item2, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('item1', $item1), 'Item 1 is cached.');
+ $this->assertTrue($this->checkCacheExists('item2', $item2), 'Item 2 is cached.');
+
+ // Fetch both records from the database with cache_get_multiple().
+ $item_ids = array('item1', 'item2');
+ $items = cache_get_multiple($item_ids, $this->default_bin);
+ $this->assertEqual($items['item1']->data, $item1, 'Item was returned from cache successfully.');
+ $this->assertEqual($items['item2']->data, $item2, 'Item was returned from cache successfully.');
+
+ // Remove one item from the cache.
+ cache_clear_all('item2', $this->default_bin);
+
+ // Confirm that only one item is returned by cache_get_multiple().
+ $item_ids = array('item1', 'item2');
+ $items = cache_get_multiple($item_ids, $this->default_bin);
+ $this->assertEqual($items['item1']->data, $item1, 'Item was returned from cache successfully.');
+ $this->assertFalse(isset($items['item2']), 'Item was not returned from the cache.');
+ $this->assertTrue(count($items) == 1, 'Only valid cache entries returned.');
+ }
+}
+
+/**
+ * Test cache clearing methods.
+ */
+class CacheClearCase extends CacheTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Cache clear test',
+ 'description' => 'Check our clearing is done the proper way.',
+ 'group' => 'Cache'
+ );
+ }
+
+ function setUp() {
+ $this->default_bin = 'cache_page';
+ $this->default_value = $this->randomName(10);
+
+ parent::setUp();
+ }
+
+ /**
+ * Test clearing using a cid.
+ */
+ function testClearCid() {
+ cache_set('test_cid_clear', $this->default_value, $this->default_bin);
+
+ $this->assertCacheExists(t('Cache was set for clearing cid.'), $this->default_value, 'test_cid_clear');
+ cache_clear_all('test_cid_clear', $this->default_bin);
+
+ $this->assertCacheRemoved(t('Cache was removed after clearing cid.'), 'test_cid_clear');
+
+ cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ 'Two caches were created for checking cid "*" with wildcard false.');
+ cache_clear_all('*', $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ 'Two caches still exists after clearing cid "*" with wildcard false.');
+ }
+
+ /**
+ * Test clearing using wildcard.
+ */
+ function testClearWildcard() {
+ cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ 'Two caches were created for checking cid "*" with wildcard true.');
+ cache_clear_all('*', $this->default_bin, TRUE);
+ $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ 'Two caches removed after clearing cid "*" with wildcard true.');
+
+ cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ 'Two caches were created for checking cid substring with wildcard true.');
+ cache_clear_all('test_', $this->default_bin, TRUE);
+ $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ 'Two caches removed after clearing cid substring with wildcard true.');
+ }
+
+ /**
+ * Test clearing using an array.
+ */
+ function testClearArray() {
+ // Create three cache entries.
+ cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear3', $this->default_value, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear2', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear3', $this->default_value),
+ 'Three cache entries were created.');
+
+ // Clear two entries using an array.
+ cache_clear_all(array('test_cid_clear1', 'test_cid_clear2'), $this->default_bin);
+ $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ || $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ 'Two cache entries removed after clearing with an array.');
+
+ $this->assertTrue($this->checkCacheExists('test_cid_clear3', $this->default_value),
+ 'Entry was not cleared from the cache');
+
+ // Set the cache clear threshold to 2 to confirm that the full bin is cleared
+ // when the threshold is exceeded.
+ variable_set('cache_clear_threshold', 2);
+ cache_set('test_cid_clear1', $this->default_value, $this->default_bin);
+ cache_set('test_cid_clear2', $this->default_value, $this->default_bin);
+ $this->assertTrue($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ && $this->checkCacheExists('test_cid_clear2', $this->default_value),
+ 'Two cache entries were created.');
+ cache_clear_all(array('test_cid_clear1', 'test_cid_clear2', 'test_cid_clear3'), $this->default_bin);
+ $this->assertFalse($this->checkCacheExists('test_cid_clear1', $this->default_value)
+ || $this->checkCacheExists('test_cid_clear2', $this->default_value)
+ || $this->checkCacheExists('test_cid_clear3', $this->default_value),
+ 'All cache entries removed when the array exceeded the cache clear threshold.');
+ }
+
+ /**
+ * Test drupal_flush_all_caches().
+ */
+ function testFlushAllCaches() {
+ // Create cache entries for each flushed cache bin.
+ $bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path');
+ $bins = array_merge(module_invoke_all('flush_caches'), $bins);
+ foreach ($bins as $id => $bin) {
+ $id = 'test_cid_clear' . $id;
+ cache_set($id, $this->default_value, $bin);
+ }
+
+ // Remove all caches then make sure that they are cleared.
+ drupal_flush_all_caches();
+
+ foreach ($bins as $id => $bin) {
+ $id = 'test_cid_clear' . $id;
+ $this->assertFalse($this->checkCacheExists($id, $this->default_value, $bin), format_string('All cache entries removed from @bin.', array('@bin' => $bin)));
+ }
+ }
+
+ /**
+ * Test DrupalDatabaseCache::isValidBin().
+ */
+ function testIsValidBin() {
+ // Retrieve existing cache bins.
+ $valid_bins = array('cache', 'cache_filter', 'cache_page', 'cache_boostrap', 'cache_path');
+ $valid_bins = array_merge(module_invoke_all('flush_caches'), $valid_bins);
+ foreach ($valid_bins as $id => $bin) {
+ $cache = _cache_get_object($bin);
+ if ($cache instanceof DrupalDatabaseCache) {
+ $this->assertTrue($cache->isValidBin(), format_string('Cache bin @bin is valid.', array('@bin' => $bin)));
+ }
+ }
+
+ // Check for non-cache tables and invalid bins.
+ $invalid_bins = array('block', 'filter', 'missing_table', $this->randomName());
+ foreach ($invalid_bins as $id => $bin) {
+ $cache = _cache_get_object($bin);
+ if ($cache instanceof DrupalDatabaseCache) {
+ $this->assertFalse($cache->isValidBin(), format_string('Cache bin @bin is not valid.', array('@bin' => $bin)));
+ }
+ }
+ }
+
+ /**
+ * Test minimum cache lifetime.
+ */
+ function testMinimumCacheLifetime() {
+ // Set a minimum/maximum cache lifetime.
+ $this->setupLifetime(300);
+ // Login as a newly-created user.
+ $account = $this->drupalCreateUser(array());
+ $this->drupalLogin($account);
+
+ // Set two cache objects in different bins.
+ $data = $this->randomName(100);
+ cache_set($data, $data, 'cache', CACHE_TEMPORARY);
+ $cached = cache_get($data);
+ $this->assertTrue(isset($cached->data) && $cached->data === $data, 'Cached item retrieved.');
+ cache_set($data, $data, 'cache_page', CACHE_TEMPORARY);
+
+ // Expire temporary items in the 'page' bin.
+ cache_clear_all(NULL, 'cache_page');
+
+ // Since the database cache uses REQUEST_TIME, set the $_SESSION variable
+ // manually to force it to the current time.
+ $_SESSION['cache_expiration']['cache_page'] = time();
+
+ // Items in the default cache bin should not be expired.
+ $cached = cache_get($data);
+ $this->assertTrue(isset($cached->data) && $cached->data == $data, 'Cached item retrieved');
+
+ // Despite the minimum cache lifetime, the item in the 'page' bin should
+ // be invalidated for the current user.
+ $cached = cache_get($data, 'cache_page');
+ $this->assertFalse($cached, 'Cached item was invalidated');
+ }
+}
+
+/**
+ * Test cache_is_empty() function.
+ */
+class CacheIsEmptyCase extends CacheTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Cache emptiness test',
+ 'description' => 'Check if a cache bin is empty after performing clear operations.',
+ 'group' => 'Cache'
+ );
+ }
+
+ function setUp() {
+ $this->default_bin = 'cache_page';
+ $this->default_value = $this->randomName(10);
+
+ parent::setUp();
+ }
+
+ /**
+ * Test clearing using a cid.
+ */
+ function testIsEmpty() {
+ // Clear the cache bin.
+ cache_clear_all('*', $this->default_bin);
+ $this->assertTrue(cache_is_empty($this->default_bin), 'The cache bin is empty');
+ // Add some data to the cache bin.
+ cache_set($this->default_cid, $this->default_value, $this->default_bin);
+ $this->assertCacheExists(t('Cache was set.'), $this->default_value, $this->default_cid);
+ $this->assertFalse(cache_is_empty($this->default_bin), 'The cache bin is not empty');
+ // Remove the cached data.
+ cache_clear_all($this->default_cid, $this->default_bin);
+ $this->assertCacheRemoved(t('Cache was removed.'), $this->default_cid);
+ $this->assertTrue(cache_is_empty($this->default_bin), 'The cache bin is empty');
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/common.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common.test
new file mode 100644
index 0000000..44eecdc
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common.test
@@ -0,0 +1,2787 @@
+<?php
+
+/**
+ * @file
+ * Tests for common.inc functionality.
+ */
+
+/**
+ * Tests for URL generation functions.
+ */
+class DrupalAlterTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'drupal_alter() tests',
+ 'description' => 'Confirm that alteration of arguments passed to drupal_alter() works correctly.',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('common_test');
+ }
+
+ function testDrupalAlter() {
+ // This test depends on Bartik, so make sure that it is always the current
+ // active theme.
+ global $theme, $base_theme_info;
+ $theme = 'bartik';
+ $base_theme_info = array();
+
+ $array = array('foo' => 'bar');
+ $entity = new stdClass();
+ $entity->foo = 'bar';
+
+ // Verify alteration of a single argument.
+ $array_copy = $array;
+ $array_expected = array('foo' => 'Drupal theme');
+ drupal_alter('drupal_alter', $array_copy);
+ $this->assertEqual($array_copy, $array_expected, 'Single array was altered.');
+
+ $entity_copy = clone $entity;
+ $entity_expected = clone $entity;
+ $entity_expected->foo = 'Drupal theme';
+ drupal_alter('drupal_alter', $entity_copy);
+ $this->assertEqual($entity_copy, $entity_expected, 'Single object was altered.');
+
+ // Verify alteration of multiple arguments.
+ $array_copy = $array;
+ $array_expected = array('foo' => 'Drupal theme');
+ $entity_copy = clone $entity;
+ $entity_expected = clone $entity;
+ $entity_expected->foo = 'Drupal theme';
+ $array2_copy = $array;
+ $array2_expected = array('foo' => 'Drupal theme');
+ drupal_alter('drupal_alter', $array_copy, $entity_copy, $array2_copy);
+ $this->assertEqual($array_copy, $array_expected, 'First argument to drupal_alter() was altered.');
+ $this->assertEqual($entity_copy, $entity_expected, 'Second argument to drupal_alter() was altered.');
+ $this->assertEqual($array2_copy, $array2_expected, 'Third argument to drupal_alter() was altered.');
+
+ // Verify alteration order when passing an array of types to drupal_alter().
+ // common_test_module_implements_alter() places 'block' implementation after
+ // other modules.
+ $array_copy = $array;
+ $array_expected = array('foo' => 'Drupal block theme');
+ drupal_alter(array('drupal_alter', 'drupal_alter_foo'), $array_copy);
+ $this->assertEqual($array_copy, $array_expected, 'hook_TYPE_alter() implementations ran in correct order.');
+ }
+}
+
+/**
+ * Tests for URL generation functions.
+ *
+ * url() calls module_implements(), which may issue a db query, which requires
+ * inheriting from a web test case rather than a unit test case.
+ */
+class CommonURLUnitTest extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'URL generation tests',
+ 'description' => 'Confirm that url(), drupal_get_query_parameters(), drupal_http_build_query(), and l() work correctly with various input.',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Confirm that invalid text given as $path is filtered.
+ */
+ function testLXSS() {
+ $text = $this->randomName();
+ $path = "<SCRIPT>alert('XSS')</SCRIPT>";
+ $link = l($text, $path);
+ $sanitized_path = check_url(url($path));
+ $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered', array('@path' => $path)));
+ }
+
+ /*
+ * Tests for active class in l() function.
+ */
+ function testLActiveClass() {
+ $link = l($this->randomName(), $_GET['q']);
+ $this->assertTrue($this->hasClass($link, 'active'), format_string('Class @class is present on link to the current page', array('@class' => 'active')));
+ }
+
+ /**
+ * Tests for custom class in l() function.
+ */
+ function testLCustomClass() {
+ $class = $this->randomName();
+ $link = l($this->randomName(), $_GET['q'], array('attributes' => array('class' => array($class))));
+ $this->assertTrue($this->hasClass($link, $class), format_string('Custom class @class is present on link when requested', array('@class' => $class)));
+ $this->assertTrue($this->hasClass($link, 'active'), format_string('Class @class is present on link to the current page', array('@class' => 'active')));
+ }
+
+ private function hasClass($link, $class) {
+ return preg_match('|class="([^\"\s]+\s+)*' . $class . '|', $link);
+ }
+
+ /**
+ * Test drupal_get_query_parameters().
+ */
+ function testDrupalGetQueryParameters() {
+ $original = array(
+ 'a' => 1,
+ 'b' => array(
+ 'd' => 4,
+ 'e' => array(
+ 'f' => 5,
+ ),
+ ),
+ 'c' => 3,
+ 'q' => 'foo/bar',
+ );
+
+ // Default arguments.
+ $result = $_GET;
+ unset($result['q']);
+ $this->assertEqual(drupal_get_query_parameters(), $result, "\$_GET['q'] was removed.");
+
+ // Default exclusion.
+ $result = $original;
+ unset($result['q']);
+ $this->assertEqual(drupal_get_query_parameters($original), $result, "'q' was removed.");
+
+ // First-level exclusion.
+ $result = $original;
+ unset($result['b']);
+ $this->assertEqual(drupal_get_query_parameters($original, array('b')), $result, "'b' was removed.");
+
+ // Second-level exclusion.
+ $result = $original;
+ unset($result['b']['d']);
+ $this->assertEqual(drupal_get_query_parameters($original, array('b[d]')), $result, "'b[d]' was removed.");
+
+ // Third-level exclusion.
+ $result = $original;
+ unset($result['b']['e']['f']);
+ $this->assertEqual(drupal_get_query_parameters($original, array('b[e][f]')), $result, "'b[e][f]' was removed.");
+
+ // Multiple exclusions.
+ $result = $original;
+ unset($result['a'], $result['b']['e'], $result['c']);
+ $this->assertEqual(drupal_get_query_parameters($original, array('a', 'b[e]', 'c')), $result, "'a', 'b[e]', 'c' were removed.");
+ }
+
+ /**
+ * Test drupal_http_build_query().
+ */
+ function testDrupalHttpBuildQuery() {
+ $this->assertEqual(drupal_http_build_query(array('a' => ' &#//+%20@۞')), 'a=%20%26%23//%2B%2520%40%DB%9E', 'Value was properly encoded.');
+ $this->assertEqual(drupal_http_build_query(array(' &#//+%20@۞' => 'a')), '%20%26%23%2F%2F%2B%2520%40%DB%9E=a', 'Key was properly encoded.');
+ $this->assertEqual(drupal_http_build_query(array('a' => '1', 'b' => '2', 'c' => '3')), 'a=1&b=2&c=3', 'Multiple values were properly concatenated.');
+ $this->assertEqual(drupal_http_build_query(array('a' => array('b' => '2', 'c' => '3'), 'd' => 'foo')), 'a[b]=2&a[c]=3&d=foo', 'Nested array was properly encoded.');
+ }
+
+ /**
+ * Test drupal_parse_url().
+ */
+ function testDrupalParseUrl() {
+ // Relative URL.
+ $url = 'foo/bar?foo=bar&bar=baz&baz#foo';
+ $result = array(
+ 'path' => 'foo/bar',
+ 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
+ 'fragment' => 'foo',
+ );
+ $this->assertEqual(drupal_parse_url($url), $result, 'Relative URL parsed correctly.');
+
+ // Relative URL that is known to confuse parse_url().
+ $url = 'foo/bar:1';
+ $result = array(
+ 'path' => 'foo/bar:1',
+ 'query' => array(),
+ 'fragment' => '',
+ );
+ $this->assertEqual(drupal_parse_url($url), $result, 'Relative URL parsed correctly.');
+
+ // Absolute URL.
+ $url = '/foo/bar?foo=bar&bar=baz&baz#foo';
+ $result = array(
+ 'path' => '/foo/bar',
+ 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
+ 'fragment' => 'foo',
+ );
+ $this->assertEqual(drupal_parse_url($url), $result, 'Absolute URL parsed correctly.');
+
+ // External URL testing.
+ $url = 'http://drupal.org/foo/bar?foo=bar&bar=baz&baz#foo';
+
+ // Test that drupal can recognize an absolute URL. Used to prevent attack vectors.
+ $this->assertTrue(url_is_external($url), 'Correctly identified an external URL.');
+
+ // Test the parsing of absolute URLs.
+ $result = array(
+ 'path' => 'http://drupal.org/foo/bar',
+ 'query' => array('foo' => 'bar', 'bar' => 'baz', 'baz' => ''),
+ 'fragment' => 'foo',
+ );
+ $this->assertEqual(drupal_parse_url($url), $result, 'External URL parsed correctly.');
+
+ // Verify proper parsing of URLs when clean URLs are disabled.
+ $result = array(
+ 'path' => 'foo/bar',
+ 'query' => array('bar' => 'baz'),
+ 'fragment' => 'foo',
+ );
+ // Non-clean URLs #1: Absolute URL generated by url().
+ $url = $GLOBALS['base_url'] . '/?q=foo/bar&bar=baz#foo';
+ $this->assertEqual(drupal_parse_url($url), $result, 'Absolute URL with clean URLs disabled parsed correctly.');
+
+ // Non-clean URLs #2: Relative URL generated by url().
+ $url = '?q=foo/bar&bar=baz#foo';
+ $this->assertEqual(drupal_parse_url($url), $result, 'Relative URL with clean URLs disabled parsed correctly.');
+
+ // Non-clean URLs #3: URL generated by url() on non-Apache webserver.
+ $url = 'index.php?q=foo/bar&bar=baz#foo';
+ $this->assertEqual(drupal_parse_url($url), $result, 'Relative URL on non-Apache webserver with clean URLs disabled parsed correctly.');
+
+ // Test that drupal_parse_url() does not allow spoofing a URL to force a malicious redirect.
+ $parts = drupal_parse_url('forged:http://cwe.mitre.org/data/definitions/601.html');
+ $this->assertFalse(valid_url($parts['path'], TRUE), 'drupal_parse_url() correctly parsed a forged URL.');
+ }
+
+ /**
+ * Test url() with/without query, with/without fragment, absolute on/off and
+ * assert all that works when clean URLs are on and off.
+ */
+ function testUrl() {
+ global $base_url;
+
+ foreach (array(FALSE, TRUE) as $absolute) {
+ // Get the expected start of the path string.
+ $base = $absolute ? $base_url . '/' : base_path();
+ $absolute_string = $absolute ? 'absolute' : NULL;
+
+ // Disable Clean URLs.
+ $GLOBALS['conf']['clean_url'] = 0;
+
+ $url = $base . '?q=node/123';
+ $result = url('node/123', array('absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . '?q=node/123#foo';
+ $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . '?q=node/123&foo';
+ $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . '?q=node/123&foo=bar&bar=baz';
+ $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . '?q=node/123&foo#bar';
+ $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . '?q=node/123&foo#0';
+ $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '0', 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . '?q=node/123&foo';
+ $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => '', 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base;
+ $result = url('<front>', array('absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ // Enable Clean URLs.
+ $GLOBALS['conf']['clean_url'] = 1;
+
+ $url = $base . 'node/123';
+ $result = url('node/123', array('absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . 'node/123#foo';
+ $result = url('node/123', array('fragment' => 'foo', 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . 'node/123?foo';
+ $result = url('node/123', array('query' => array('foo' => NULL), 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . 'node/123?foo=bar&bar=baz';
+ $result = url('node/123', array('query' => array('foo' => 'bar', 'bar' => 'baz'), 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base . 'node/123?foo#bar';
+ $result = url('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+
+ $url = $base;
+ $result = url('<front>', array('absolute' => $absolute));
+ $this->assertEqual($url, $result, "$url == $result");
+ }
+ }
+
+ /**
+ * Test external URL handling.
+ */
+ function testExternalUrls() {
+ $test_url = 'http://drupal.org/';
+
+ // Verify external URL can contain a fragment.
+ $url = $test_url . '#drupal';
+ $result = url($url);
+ $this->assertEqual($url, $result, 'External URL with fragment works without a fragment in $options.');
+
+ // Verify fragment can be overidden in an external URL.
+ $url = $test_url . '#drupal';
+ $fragment = $this->randomName(10);
+ $result = url($url, array('fragment' => $fragment));
+ $this->assertEqual($test_url . '#' . $fragment, $result, 'External URL fragment is overidden with a custom fragment in $options.');
+
+ // Verify external URL can contain a query string.
+ $url = $test_url . '?drupal=awesome';
+ $result = url($url);
+ $this->assertEqual($url, $result, 'External URL with query string works without a query string in $options.');
+
+ // Verify external URL can be extended with a query string.
+ $url = $test_url;
+ $query = array($this->randomName(5) => $this->randomName(5));
+ $result = url($url, array('query' => $query));
+ $this->assertEqual($url . '?' . http_build_query($query, '', '&'), $result, 'External URL can be extended with a query string in $options.');
+
+ // Verify query string can be extended in an external URL.
+ $url = $test_url . '?drupal=awesome';
+ $query = array($this->randomName(5) => $this->randomName(5));
+ $result = url($url, array('query' => $query));
+ $this->assertEqual($url . '&' . http_build_query($query, '', '&'), $result, 'External URL query string can be extended with a custom query string in $options.');
+ }
+}
+
+/**
+ * Tests for check_plain(), filter_xss(), format_string(), and check_url().
+ */
+class CommonXssUnitTest extends DrupalUnitTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'String filtering tests',
+ 'description' => 'Confirm that check_plain(), filter_xss(), format_string() and check_url() work correctly, including invalid multi-byte sequences.',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Check that invalid multi-byte sequences are rejected.
+ */
+ function testInvalidMultiByte() {
+ // Ignore PHP 5.3+ invalid multibyte sequence warning.
+ $text = @check_plain("Foo\xC0barbaz");
+ $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "Foo\xC0barbaz"');
+ // Ignore PHP 5.3+ invalid multibyte sequence warning.
+ $text = @check_plain("\xc2\"");
+ $this->assertEqual($text, '', 'check_plain() rejects invalid sequence "\xc2\""');
+ $text = check_plain("Fooÿñ");
+ $this->assertEqual($text, "Fooÿñ", 'check_plain() accepts valid sequence "Fooÿñ"');
+ $text = filter_xss("Foo\xC0barbaz");
+ $this->assertEqual($text, '', 'filter_xss() rejects invalid sequence "Foo\xC0barbaz"');
+ $text = filter_xss("Fooÿñ");
+ $this->assertEqual($text, "Fooÿñ", 'filter_xss() accepts valid sequence Fooÿñ');
+ }
+
+ /**
+ * Check that special characters are escaped.
+ */
+ function testEscaping() {
+ $text = check_plain("<script>");
+ $this->assertEqual($text, '&lt;script&gt;', 'check_plain() escapes &lt;script&gt;');
+ $text = check_plain('<>&"\'');
+ $this->assertEqual($text, '&lt;&gt;&amp;&quot;&#039;', 'check_plain() escapes reserved HTML characters.');
+ }
+
+ /**
+ * Test t() and format_string() replacement functionality.
+ */
+ function testFormatStringAndT() {
+ foreach (array('format_string', 't') as $function) {
+ $text = $function('Simple text');
+ $this->assertEqual($text, 'Simple text', $function . ' leaves simple text alone.');
+ $text = $function('Escaped text: @value', array('@value' => '<script>'));
+ $this->assertEqual($text, 'Escaped text: &lt;script&gt;', $function . ' replaces and escapes string.');
+ $text = $function('Placeholder text: %value', array('%value' => '<script>'));
+ $this->assertEqual($text, 'Placeholder text: <em class="placeholder">&lt;script&gt;</em>', $function . ' replaces, escapes and themes string.');
+ $text = $function('Verbatim text: !value', array('!value' => '<script>'));
+ $this->assertEqual($text, 'Verbatim text: <script>', $function . ' replaces verbatim string as-is.');
+ }
+ }
+
+ /**
+ * Check that harmful protocols are stripped.
+ */
+ function testBadProtocolStripping() {
+ // Ensure that check_url() strips out harmful protocols, and encodes for
+ // HTML. Ensure drupal_strip_dangerous_protocols() can be used to return a
+ // plain-text string stripped of harmful protocols.
+ $url = 'javascript:http://www.example.com/?x=1&y=2';
+ $expected_plain = 'http://www.example.com/?x=1&y=2';
+ $expected_html = 'http://www.example.com/?x=1&amp;y=2';
+ $this->assertIdentical(check_url($url), $expected_html, 'check_url() filters a URL and encodes it for HTML.');
+ $this->assertIdentical(drupal_strip_dangerous_protocols($url), $expected_plain, 'drupal_strip_dangerous_protocols() filters a URL and returns plain text.');
+ }
+}
+
+/**
+ * Tests file size parsing and formatting functions.
+ */
+class CommonSizeTestCase extends DrupalUnitTestCase {
+ protected $exact_test_cases;
+ protected $rounded_test_cases;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Size parsing test',
+ 'description' => 'Parse a predefined amount of bytes and compare the output with the expected value.',
+ 'group' => 'System'
+ );
+ }
+
+ function setUp() {
+ $kb = DRUPAL_KILOBYTE;
+ $this->exact_test_cases = array(
+ '1 byte' => 1,
+ '1 KB' => $kb,
+ '1 MB' => $kb * $kb,
+ '1 GB' => $kb * $kb * $kb,
+ '1 TB' => $kb * $kb * $kb * $kb,
+ '1 PB' => $kb * $kb * $kb * $kb * $kb,
+ '1 EB' => $kb * $kb * $kb * $kb * $kb * $kb,
+ '1 ZB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb,
+ '1 YB' => $kb * $kb * $kb * $kb * $kb * $kb * $kb * $kb,
+ );
+ $this->rounded_test_cases = array(
+ '2 bytes' => 2,
+ '1 MB' => ($kb * $kb) - 1, // rounded to 1 MB (not 1000 or 1024 kilobyte!)
+ round(3623651 / ($this->exact_test_cases['1 MB']), 2) . ' MB' => 3623651, // megabytes
+ round(67234178751368124 / ($this->exact_test_cases['1 PB']), 2) . ' PB' => 67234178751368124, // petabytes
+ round(235346823821125814962843827 / ($this->exact_test_cases['1 YB']), 2) . ' YB' => 235346823821125814962843827, // yottabytes
+ );
+ parent::setUp();
+ }
+
+ /**
+ * Check that format_size() returns the expected string.
+ */
+ function testCommonFormatSize() {
+ foreach (array($this->exact_test_cases, $this->rounded_test_cases) as $test_cases) {
+ foreach ($test_cases as $expected => $input) {
+ $this->assertEqual(
+ ($result = format_size($input, NULL)),
+ $expected,
+ $expected . ' == ' . $result . ' (' . $input . ' bytes)'
+ );
+ }
+ }
+ }
+
+ /**
+ * Check that parse_size() returns the proper byte sizes.
+ */
+ function testCommonParseSize() {
+ foreach ($this->exact_test_cases as $string => $size) {
+ $this->assertEqual(
+ $parsed_size = parse_size($string),
+ $size,
+ $size . ' == ' . $parsed_size . ' (' . $string . ')'
+ );
+ }
+
+ // Some custom parsing tests
+ $string = '23476892 bytes';
+ $this->assertEqual(
+ ($parsed_size = parse_size($string)),
+ $size = 23476892,
+ $string . ' == ' . $parsed_size . ' bytes'
+ );
+ $string = '76MRandomStringThatShouldBeIgnoredByParseSize.'; // 76 MB
+ $this->assertEqual(
+ $parsed_size = parse_size($string),
+ $size = 79691776,
+ $string . ' == ' . $parsed_size . ' bytes'
+ );
+ $string = '76.24 Giggabyte'; // Misspeld text -> 76.24 GB
+ $this->assertEqual(
+ $parsed_size = parse_size($string),
+ $size = 81862076662,
+ $string . ' == ' . $parsed_size . ' bytes'
+ );
+ }
+
+ /**
+ * Cross-test parse_size() and format_size().
+ */
+ function testCommonParseSizeFormatSize() {
+ foreach ($this->exact_test_cases as $size) {
+ $this->assertEqual(
+ $size,
+ ($parsed_size = parse_size($string = format_size($size, NULL))),
+ $size . ' == ' . $parsed_size . ' (' . $string . ')'
+ );
+ }
+ }
+}
+
+/**
+ * Test drupal_explode_tags() and drupal_implode_tags().
+ */
+class DrupalTagsHandlingTestCase extends DrupalUnitTestCase {
+ var $validTags = array(
+ 'Drupal' => 'Drupal',
+ 'Drupal with some spaces' => 'Drupal with some spaces',
+ '"Legendary Drupal mascot of doom: ""Druplicon"""' => 'Legendary Drupal mascot of doom: "Druplicon"',
+ '"Drupal, although it rhymes with sloopal, is as awesome as a troopal!"' => 'Drupal, although it rhymes with sloopal, is as awesome as a troopal!',
+ );
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Drupal tags handling',
+ 'description' => "Performs tests on Drupal's handling of tags, both explosion and implosion tactics used.",
+ 'group' => 'System'
+ );
+ }
+
+ /**
+ * Explode a series of tags.
+ */
+ function testDrupalExplodeTags() {
+ $string = implode(', ', array_keys($this->validTags));
+ $tags = drupal_explode_tags($string);
+ $this->assertTags($tags);
+ }
+
+ /**
+ * Implode a series of tags.
+ */
+ function testDrupalImplodeTags() {
+ $tags = array_values($this->validTags);
+ // Let's explode and implode to our heart's content.
+ for ($i = 0; $i < 10; $i++) {
+ $string = drupal_implode_tags($tags);
+ $tags = drupal_explode_tags($string);
+ }
+ $this->assertTags($tags);
+ }
+
+ /**
+ * Helper function: asserts that the ending array of tags is what we wanted.
+ */
+ function assertTags($tags) {
+ $original = $this->validTags;
+ foreach ($tags as $tag) {
+ $key = array_search($tag, $original);
+ $this->assertTrue($key, format_string('Make sure tag %tag shows up in the final tags array (originally %original)', array('%tag' => $tag, '%original' => $key)));
+ unset($original[$key]);
+ }
+ foreach ($original as $leftover) {
+ $this->fail(format_string('Leftover tag %leftover was left over.', array('%leftover' => $leftover)));
+ }
+ }
+}
+
+/**
+ * Test the Drupal CSS system.
+ */
+class CascadingStylesheetsTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Cascading stylesheets',
+ 'description' => 'Tests adding various cascading stylesheets to the page.',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('php', 'locale', 'common_test');
+ // Reset drupal_add_css() before each test.
+ drupal_static_reset('drupal_add_css');
+ }
+
+ /**
+ * Check default stylesheets as empty.
+ */
+ function testDefault() {
+ $this->assertEqual(array(), drupal_add_css(), 'Default CSS is empty.');
+ }
+
+ /**
+ * Test that stylesheets in module .info files are loaded.
+ */
+ function testModuleInfo() {
+ $this->drupalGet('');
+
+ // Verify common_test.css in a STYLE media="all" tag.
+ $elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array(
+ ':media' => 'all',
+ ':filename' => 'tests/common_test.css',
+ ));
+ $this->assertTrue(count($elements), "Stylesheet with media 'all' in module .info file found.");
+
+ // Verify common_test.print.css in a STYLE media="print" tag.
+ $elements = $this->xpath('//style[@media=:media and contains(text(), :filename)]', array(
+ ':media' => 'print',
+ ':filename' => 'tests/common_test.print.css',
+ ));
+ $this->assertTrue(count($elements), "Stylesheet with media 'print' in module .info file found.");
+ }
+
+ /**
+ * Tests adding a file stylesheet.
+ */
+ function testAddFile() {
+ $path = drupal_get_path('module', 'simpletest') . '/simpletest.css';
+ $css = drupal_add_css($path);
+ $this->assertEqual($css[$path]['data'], $path, 'Adding a CSS file caches it properly.');
+ }
+
+ /**
+ * Tests adding an external stylesheet.
+ */
+ function testAddExternal() {
+ $path = 'http://example.com/style.css';
+ $css = drupal_add_css($path, 'external');
+ $this->assertEqual($css[$path]['type'], 'external', 'Adding an external CSS file caches it properly.');
+ }
+
+ /**
+ * Makes sure that reseting the CSS empties the cache.
+ */
+ function testReset() {
+ drupal_static_reset('drupal_add_css');
+ $this->assertEqual(array(), drupal_add_css(), 'Resetting the CSS empties the cache.');
+ }
+
+ /**
+ * Tests rendering the stylesheets.
+ */
+ function testRenderFile() {
+ $css = drupal_get_path('module', 'simpletest') . '/simpletest.css';
+ drupal_add_css($css);
+ $styles = drupal_get_css();
+ $this->assertTrue(strpos($styles, $css) > 0, 'Rendered CSS includes the added stylesheet.');
+ }
+
+ /**
+ * Tests rendering an external stylesheet.
+ */
+ function testRenderExternal() {
+ $css = 'http://example.com/style.css';
+ drupal_add_css($css, 'external');
+ $styles = drupal_get_css();
+ // Stylesheet URL may be the href of a LINK tag or in an @import statement
+ // of a STYLE tag.
+ $this->assertTrue(strpos($styles, 'href="' . $css) > 0 || strpos($styles, '@import url("' . $css . '")') > 0, 'Rendering an external CSS file.');
+ }
+
+ /**
+ * Tests rendering inline stylesheets with preprocessing on.
+ */
+ function testRenderInlinePreprocess() {
+ $css = 'body { padding: 0px; }';
+ $css_preprocessed = '<style type="text/css" media="all">' . "\n<!--/*--><![CDATA[/*><!--*/\n" . drupal_load_stylesheet_content($css, TRUE) . "\n/*]]>*/-->\n" . '</style>';
+ drupal_add_css($css, array('type' => 'inline'));
+ $styles = drupal_get_css();
+ $this->assertEqual(trim($styles), $css_preprocessed, 'Rendering preprocessed inline CSS adds it to the page.');
+ }
+
+ /**
+ * Tests removing charset when rendering stylesheets with preprocessing on.
+ */
+ function testRenderRemoveCharsetPreprocess() {
+ $cases = array(
+ array(
+ 'asset' => '@charset "UTF-8";html{font-family:"sans-serif";}',
+ 'expected' => 'html{font-family:"sans-serif";}',
+ ),
+ // This asset contains extra \n character.
+ array(
+ 'asset' => "@charset 'UTF-8';\nhtml{font-family:'sans-serif';}",
+ 'expected' => "\nhtml{font-family:'sans-serif';}",
+ ),
+ );
+
+ foreach ($cases as $case) {
+ $this->assertEqual(
+ $case['expected'],
+ drupal_load_stylesheet_content($case['asset']),
+ 'CSS optimizing correctly removes the charset declaration.'
+ );
+ }
+ }
+
+ /**
+ * Tests rendering inline stylesheets with preprocessing off.
+ */
+ function testRenderInlineNoPreprocess() {
+ $css = 'body { padding: 0px; }';
+ drupal_add_css($css, array('type' => 'inline', 'preprocess' => FALSE));
+ $styles = drupal_get_css();
+ $this->assertTrue(strpos($styles, $css) > 0, 'Rendering non-preprocessed inline CSS adds it to the page.');
+ }
+
+ /**
+ * Tests rendering inline stylesheets through a full page request.
+ */
+ function testRenderInlineFullPage() {
+ $css = 'body { font-size: 254px; }';
+ // Inline CSS is minified unless 'preprocess' => FALSE is passed as a
+ // drupal_add_css() option.
+ $expected = 'body{font-size:254px;}';
+
+ // Create a node, using the PHP filter that tests drupal_add_css().
+ $php_format_id = 'php_code';
+ $settings = array(
+ 'type' => 'page',
+ 'body' => array(
+ LANGUAGE_NONE => array(
+ array(
+ 'value' => t('This tests the inline CSS!') . "<?php drupal_add_css('$css', 'inline'); ?>",
+ 'format' => $php_format_id,
+ ),
+ ),
+ ),
+ 'promote' => 1,
+ );
+ $node = $this->drupalCreateNode($settings);
+
+ // Fetch the page.
+ $this->drupalGet('node/' . $node->nid);
+ $this->assertRaw($expected, 'Inline stylesheets appear in the full page rendering.');
+ }
+
+ /**
+ * Test CSS ordering.
+ */
+ function testRenderOrder() {
+ // A module CSS file.
+ drupal_add_css(drupal_get_path('module', 'simpletest') . '/simpletest.css');
+ // A few system CSS files, ordered in a strange way.
+ $system_path = drupal_get_path('module', 'system');
+ drupal_add_css($system_path . '/system.menus.css', array('group' => CSS_SYSTEM));
+ drupal_add_css($system_path . '/system.base.css', array('group' => CSS_SYSTEM, 'weight' => -10));
+ drupal_add_css($system_path . '/system.theme.css', array('group' => CSS_SYSTEM));
+
+ $expected = array(
+ $system_path . '/system.base.css',
+ $system_path . '/system.menus.css',
+ $system_path . '/system.theme.css',
+ drupal_get_path('module', 'simpletest') . '/simpletest.css',
+ );
+
+
+ $styles = drupal_get_css();
+ // Stylesheet URL may be the href of a LINK tag or in an @import statement
+ // of a STYLE tag.
+ if (preg_match_all('/(href="|url\(")' . preg_quote($GLOBALS['base_url'] . '/', '/') . '([^?]+)\?/', $styles, $matches)) {
+ $result = $matches[2];
+ }
+ else {
+ $result = array();
+ }
+
+ $this->assertIdentical($result, $expected, 'The CSS files are in the expected order.');
+ }
+
+ /**
+ * Test CSS override.
+ */
+ function testRenderOverride() {
+ $system = drupal_get_path('module', 'system');
+ $simpletest = drupal_get_path('module', 'simpletest');
+
+ drupal_add_css($system . '/system.base.css');
+ drupal_add_css($simpletest . '/tests/system.base.css');
+
+ // The dummy stylesheet should be the only one included.
+ $styles = drupal_get_css();
+ $this->assert(strpos($styles, $simpletest . '/tests/system.base.css') !== FALSE, 'The overriding CSS file is output.');
+ $this->assert(strpos($styles, $system . '/system.base.css') === FALSE, 'The overridden CSS file is not output.');
+
+ drupal_add_css($simpletest . '/tests/system.base.css');
+ drupal_add_css($system . '/system.base.css');
+
+ // The standard stylesheet should be the only one included.
+ $styles = drupal_get_css();
+ $this->assert(strpos($styles, $system . '/system.base.css') !== FALSE, 'The overriding CSS file is output.');
+ $this->assert(strpos($styles, $simpletest . '/tests/system.base.css') === FALSE, 'The overridden CSS file is not output.');
+ }
+
+ /**
+ * Tests Locale module's CSS Alter to include RTL overrides.
+ */
+ function testAlter() {
+ // Switch the language to a right to left language and add system.base.css.
+ global $language;
+ $language->direction = LANGUAGE_RTL;
+ $path = drupal_get_path('module', 'system');
+ drupal_add_css($path . '/system.base.css');
+
+ // Check to see if system.base-rtl.css was also added.
+ $styles = drupal_get_css();
+ $this->assert(strpos($styles, $path . '/system.base-rtl.css') !== FALSE, 'CSS is alterable as right to left overrides are added.');
+
+ // Change the language back to left to right.
+ $language->direction = LANGUAGE_LTR;
+ }
+
+ /**
+ * Tests that the query string remains intact when adding CSS files that have
+ * query string parameters.
+ */
+ function testAddCssFileWithQueryString() {
+ $this->drupalGet('common-test/query-string');
+ $query_string = variable_get('css_js_query_string', '0');
+ $this->assertRaw(drupal_get_path('module', 'node') . '/node.css?' . $query_string, 'Query string was appended correctly to css.');
+ $this->assertRaw(drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&amp;arg2=value2', 'Query string not escaped on a URI.');
+ }
+}
+
+/**
+ * Test for cleaning HTML identifiers.
+ */
+class DrupalHTMLIdentifierTestCase extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'HTML identifiers',
+ 'description' => 'Test the functions drupal_html_class(), drupal_html_id() and drupal_clean_css_identifier() for expected behavior',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Tests that drupal_clean_css_identifier() cleans the identifier properly.
+ */
+ function testDrupalCleanCSSIdentifier() {
+ // Verify that no valid ASCII characters are stripped from the identifier.
+ $identifier = 'abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789';
+ $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, 'Verify valid ASCII characters pass through.');
+
+ // Verify that valid UTF-8 characters are not stripped from the identifier.
+ $identifier = '¡¢£¤¥';
+ $this->assertIdentical(drupal_clean_css_identifier($identifier, array()), $identifier, 'Verify valid UTF-8 characters pass through.');
+
+ // Verify that invalid characters (including non-breaking space) are stripped from the identifier.
+ $this->assertIdentical(drupal_clean_css_identifier('invalid !"#$%&\'()*+,./:;<=>?@[\\]^`{|}~ identifier', array()), 'invalididentifier', 'Strip invalid characters.');
+ }
+
+ /**
+ * Tests that drupal_html_class() cleans the class name properly.
+ */
+ function testDrupalHTMLClass() {
+ // Verify Drupal coding standards are enforced.
+ $this->assertIdentical(drupal_html_class('CLASS NAME_[Ü]'), 'class-name--ü', 'Enforce Drupal coding standards.');
+ }
+
+ /**
+ * Tests that drupal_html_id() cleans the ID properly.
+ */
+ function testDrupalHTMLId() {
+ // Verify that letters, digits, and hyphens are not stripped from the ID.
+ $id = 'abcdefghijklmnopqrstuvwxyz-0123456789';
+ $this->assertIdentical(drupal_html_id($id), $id, 'Verify valid characters pass through.');
+
+ // Verify that invalid characters are stripped from the ID.
+ $this->assertIdentical(drupal_html_id('invalid,./:@\\^`{Üidentifier'), 'invalididentifier', 'Strip invalid characters.');
+
+ // Verify Drupal coding standards are enforced.
+ $this->assertIdentical(drupal_html_id('ID NAME_[1]'), 'id-name-1', 'Enforce Drupal coding standards.');
+
+ // Reset the static cache so we can ensure the unique id count is at zero.
+ drupal_static_reset('drupal_html_id');
+
+ // Clean up IDs with invalid starting characters.
+ $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id', 'Test the uniqueness of IDs #1.');
+ $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--2', 'Test the uniqueness of IDs #2.');
+ $this->assertIdentical(drupal_html_id('test-unique-id'), 'test-unique-id--3', 'Test the uniqueness of IDs #3.');
+ }
+}
+
+/**
+ * CSS Unit Tests.
+ */
+class CascadingStylesheetsUnitTest extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'CSS Unit Tests',
+ 'description' => 'Unit tests on CSS functions like aggregation.',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Tests basic CSS loading with and without optimization via drupal_load_stylesheet().
+ *
+ * Known tests:
+ * - Retain white-space in selectors. (http://drupal.org/node/472820)
+ * - Proper URLs in imported files. (http://drupal.org/node/265719)
+ * - Retain pseudo-selectors. (http://drupal.org/node/460448)
+ */
+ function testLoadCssBasic() {
+ // Array of files to test living in 'simpletest/files/css_test_files/'.
+ // - Original: name.css
+ // - Unoptimized expected content: name.css.unoptimized.css
+ // - Optimized expected content: name.css.optimized.css
+ $testfiles = array(
+ 'css_input_without_import.css',
+ 'css_input_with_import.css',
+ 'css_subfolder/css_input_with_import.css',
+ 'comment_hacks.css'
+ );
+ $path = drupal_get_path('module', 'simpletest') . '/files/css_test_files';
+ foreach ($testfiles as $file) {
+ $file_path = $path . '/' . $file;
+ $file_url = $GLOBALS['base_url'] . '/' . $file_path;
+
+ $expected = file_get_contents($file_path . '.unoptimized.css');
+ $unoptimized_output = drupal_load_stylesheet($file_path, FALSE);
+ $this->assertEqual($unoptimized_output, $expected, format_string('Unoptimized CSS file has expected contents (@file)', array('@file' => $file)));
+
+ $expected = file_get_contents($file_path . '.optimized.css');
+ $optimized_output = drupal_load_stylesheet($file_path, TRUE);
+ $this->assertEqual($optimized_output, $expected, format_string('Optimized CSS file has expected contents (@file)', array('@file' => $file)));
+
+ // Repeat the tests by accessing the stylesheets by URL.
+ $expected = file_get_contents($file_path . '.unoptimized.css');
+ $unoptimized_output_url = drupal_load_stylesheet($file_url, FALSE);
+ $this->assertEqual($unoptimized_output_url, $expected, format_string('Unoptimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file)));
+
+ $expected = file_get_contents($file_path . '.optimized.css');
+ $optimized_output_url = drupal_load_stylesheet($file_url, TRUE);
+ $this->assertEqual($optimized_output_url, $expected, format_string('Optimized CSS file (loaded from an URL) has expected contents (@file)', array('@file' => $file)));
+ }
+ }
+}
+
+/**
+ * Test drupal_http_request().
+ */
+class DrupalHTTPRequestTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Drupal HTTP request',
+ 'description' => "Performs tests on Drupal's HTTP request mechanism.",
+ 'group' => 'System'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('system_test', 'locale');
+ }
+
+ function testDrupalHTTPRequest() {
+ global $is_https;
+
+ // Parse URL schema.
+ $missing_scheme = drupal_http_request('example.com/path');
+ $this->assertEqual($missing_scheme->code, -1002, 'Returned with "-1002" error code.');
+ $this->assertEqual($missing_scheme->error, 'missing schema', 'Returned with "missing schema" error message.');
+
+ $unable_to_parse = drupal_http_request('http:///path');
+ $this->assertEqual($unable_to_parse->code, -1001, 'Returned with "-1001" error code.');
+ $this->assertEqual($unable_to_parse->error, 'unable to parse URL', 'Returned with "unable to parse URL" error message.');
+
+ // Fetch page.
+ $result = drupal_http_request(url('node', array('absolute' => TRUE)));
+ $this->assertEqual($result->code, 200, 'Fetched page successfully.');
+ $this->drupalSetContent($result->data);
+ $this->assertTitle(t('Welcome to @site-name | @site-name', array('@site-name' => variable_get('site_name', 'Drupal'))), 'Site title matches.');
+
+ // Test that code and status message is returned.
+ $result = drupal_http_request(url('pagedoesnotexist', array('absolute' => TRUE)));
+ $this->assertTrue(!empty($result->protocol), 'Result protocol is returned.');
+ $this->assertEqual($result->code, '404', 'Result code is 404');
+ $this->assertEqual($result->status_message, 'Not Found', 'Result status message is "Not Found"');
+
+ // Skip the timeout tests when the testing environment is HTTPS because
+ // stream_set_timeout() does not work for SSL connections.
+ // @link http://bugs.php.net/bug.php?id=47929
+ if (!$is_https) {
+ // Test that timeout is respected. The test machine is expected to be able
+ // to make the connection (i.e. complete the fsockopen()) in 2 seconds and
+ // return within a total of 5 seconds. If the test machine is extremely
+ // slow, the test will fail. fsockopen() has been seen to time out in
+ // slightly less than the specified timeout, so allow a little slack on
+ // the minimum expected time (i.e. 1.8 instead of 2).
+ timer_start(__METHOD__);
+ $result = drupal_http_request(url('system-test/sleep/10', array('absolute' => TRUE)), array('timeout' => 2));
+ $time = timer_read(__METHOD__) / 1000;
+ $this->assertTrue(1.8 < $time && $time < 5, format_string('Request timed out (%time seconds).', array('%time' => $time)));
+ $this->assertTrue($result->error, 'An error message was returned.');
+ $this->assertEqual($result->code, HTTP_REQUEST_TIMEOUT, 'Proper error code was returned.');
+ }
+ }
+
+ function testDrupalHTTPRequestBasicAuth() {
+ $username = $this->randomName();
+ $password = $this->randomName();
+ $url = url('system-test/auth', array('absolute' => TRUE));
+
+ $auth = str_replace('://', '://' . $username . ':' . $password . '@', $url);
+ $result = drupal_http_request($auth);
+
+ $this->drupalSetContent($result->data);
+ $this->assertRaw($username, '$_SERVER["PHP_AUTH_USER"] is passed correctly.');
+ $this->assertRaw($password, '$_SERVER["PHP_AUTH_PW"] is passed correctly.');
+ }
+
+ function testDrupalHTTPRequestRedirect() {
+ $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 1));
+ $this->assertEqual($redirect_301->redirect_code, 301, 'drupal_http_request follows the 301 redirect.');
+
+ $redirect_301 = drupal_http_request(url('system-test/redirect/301', array('absolute' => TRUE)), array('max_redirects' => 0));
+ $this->assertFalse(isset($redirect_301->redirect_code), 'drupal_http_request does not follow 301 redirect if max_redirects = 0.');
+
+ $redirect_invalid = drupal_http_request(url('system-test/redirect-noscheme', array('absolute' => TRUE)), array('max_redirects' => 1));
+ $this->assertEqual($redirect_invalid->code, -1002, format_string('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error)));
+ $this->assertEqual($redirect_invalid->error, 'missing schema', format_string('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
+
+ $redirect_invalid = drupal_http_request(url('system-test/redirect-noparse', array('absolute' => TRUE)), array('max_redirects' => 1));
+ $this->assertEqual($redirect_invalid->code, -1001, format_string('301 redirect to invalid URL returned with error message code "!error".', array('!error' => $redirect_invalid->error)));
+ $this->assertEqual($redirect_invalid->error, 'unable to parse URL', format_string('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
+
+ $redirect_invalid = drupal_http_request(url('system-test/redirect-invalid-scheme', array('absolute' => TRUE)), array('max_redirects' => 1));
+ $this->assertEqual($redirect_invalid->code, -1003, format_string('301 redirect to invalid URL returned with error code !error.', array('!error' => $redirect_invalid->error)));
+ $this->assertEqual($redirect_invalid->error, 'invalid schema ftp', format_string('301 redirect to invalid URL returned with error message "!error".', array('!error' => $redirect_invalid->error)));
+
+ $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 1));
+ $this->assertEqual($redirect_302->redirect_code, 302, 'drupal_http_request follows the 302 redirect.');
+
+ $redirect_302 = drupal_http_request(url('system-test/redirect/302', array('absolute' => TRUE)), array('max_redirects' => 0));
+ $this->assertFalse(isset($redirect_302->redirect_code), 'drupal_http_request does not follow 302 redirect if $retry = 0.');
+
+ $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 1));
+ $this->assertEqual($redirect_307->redirect_code, 307, 'drupal_http_request follows the 307 redirect.');
+
+ $redirect_307 = drupal_http_request(url('system-test/redirect/307', array('absolute' => TRUE)), array('max_redirects' => 0));
+ $this->assertFalse(isset($redirect_307->redirect_code), 'drupal_http_request does not follow 307 redirect if max_redirects = 0.');
+
+ $multiple_redirect_final_url = url('system-test/multiple-redirects/0', array('absolute' => TRUE));
+ $multiple_redirect_1 = drupal_http_request(url('system-test/multiple-redirects/1', array('absolute' => TRUE)), array('max_redirects' => 1));
+ $this->assertEqual($multiple_redirect_1->redirect_url, $multiple_redirect_final_url, 'redirect_url contains the final redirection location after 1 redirect.');
+
+ $multiple_redirect_3 = drupal_http_request(url('system-test/multiple-redirects/3', array('absolute' => TRUE)), array('max_redirects' => 3));
+ $this->assertEqual($multiple_redirect_3->redirect_url, $multiple_redirect_final_url, 'redirect_url contains the final redirection location after 3 redirects.');
+ }
+
+ /**
+ * Tests Content-language headers generated by Drupal.
+ */
+ function testDrupalHTTPRequestHeaders() {
+ // Check the default header.
+ $request = drupal_http_request(url('<front>', array('absolute' => TRUE)));
+ $this->assertEqual($request->headers['content-language'], 'en', 'Content-Language HTTP header is English.');
+
+ // Add German language and set as default.
+ locale_add_language('de', 'German', 'Deutsch', LANGUAGE_LTR, '', '', TRUE, TRUE);
+
+ // Request front page and check for matching Content-Language.
+ $request = drupal_http_request(url('<front>', array('absolute' => TRUE)));
+ $this->assertEqual($request->headers['content-language'], 'de', 'Content-Language HTTP header is German.');
+ }
+}
+
+/**
+ * Testing drupal_add_region_content and drupal_get_region_content.
+ */
+class DrupalSetContentTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Drupal set/get regions',
+ 'description' => 'Performs tests on setting and retrieiving content from theme regions.',
+ 'group' => 'System'
+ );
+ }
+
+
+ /**
+ * Test setting and retrieving content for theme regions.
+ */
+ function testRegions() {
+ global $theme_key;
+
+ $block_regions = array_keys(system_region_list($theme_key));
+ $delimiter = $this->randomName(32);
+ $values = array();
+ // Set some random content for each region available.
+ foreach ($block_regions as $region) {
+ $first_chunk = $this->randomName(32);
+ drupal_add_region_content($region, $first_chunk);
+ $second_chunk = $this->randomName(32);
+ drupal_add_region_content($region, $second_chunk);
+ // Store the expected result for a drupal_get_region_content call for this region.
+ $values[$region] = $first_chunk . $delimiter . $second_chunk;
+ }
+
+ // Ensure drupal_get_region_content returns expected results when fetching all regions.
+ $content = drupal_get_region_content(NULL, $delimiter);
+ foreach ($content as $region => $region_content) {
+ $this->assertEqual($region_content, $values[$region], format_string('@region region text verified when fetching all regions', array('@region' => $region)));
+ }
+
+ // Ensure drupal_get_region_content returns expected results when fetching a single region.
+ foreach ($block_regions as $region) {
+ $region_content = drupal_get_region_content($region, $delimiter);
+ $this->assertEqual($region_content, $values[$region], format_string('@region region text verified when fetching single region.', array('@region' => $region)));
+ }
+ }
+}
+
+/**
+ * Testing drupal_goto and hook_drupal_goto_alter().
+ */
+class DrupalGotoTest extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Drupal goto',
+ 'description' => 'Performs tests on the drupal_goto function and hook_drupal_goto_alter',
+ 'group' => 'System'
+ );
+ }
+
+ function setUp() {
+ parent::setUp('common_test');
+ }
+
+ /**
+ * Test drupal_goto().
+ */
+ function testDrupalGoto() {
+ $this->drupalGet('common-test/drupal_goto/redirect');
+ $headers = $this->drupalGetHeaders(TRUE);
+ list(, $status) = explode(' ', $headers[0][':status'], 3);
+ $this->assertEqual($status, 302, 'Expected response code was sent.');
+ $this->assertText('drupal_goto', 'Drupal goto redirect succeeded.');
+ $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('absolute' => TRUE)), 'Drupal goto redirected to expected URL.');
+
+ $this->drupalGet('common-test/drupal_goto/redirect_advanced');
+ $headers = $this->drupalGetHeaders(TRUE);
+ list(, $status) = explode(' ', $headers[0][':status'], 3);
+ $this->assertEqual($status, 301, 'Expected response code was sent.');
+ $this->assertText('drupal_goto', 'Drupal goto redirect succeeded.');
+ $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('query' => array('foo' => '123'), 'absolute' => TRUE)), 'Drupal goto redirected to expected URL.');
+
+ // Test that drupal_goto() respects ?destination=xxx. Use an complicated URL
+ // to test that the path is encoded and decoded properly.
+ $destination = 'common-test/drupal_goto/destination?foo=%2525&bar=123';
+ $this->drupalGet('common-test/drupal_goto/redirect', array('query' => array('destination' => $destination)));
+ $this->assertText('drupal_goto', 'Drupal goto redirect with destination succeeded.');
+ $this->assertEqual($this->getUrl(), url('common-test/drupal_goto/destination', array('query' => array('foo' => '%25', 'bar' => '123'), 'absolute' => TRUE)), 'Drupal goto redirected to given query string destination.');
+ }
+
+ /**
+ * Test hook_drupal_goto_alter().
+ */
+ function testDrupalGotoAlter() {
+ $this->drupalGet('common-test/drupal_goto/redirect_fail');
+
+ $this->assertNoText(t("Drupal goto failed to stop program"), "Drupal goto stopped program.");
+ $this->assertNoText('drupal_goto_fail', "Drupal goto redirect failed.");
+ }
+
+ /**
+ * Test drupal_get_destination().
+ */
+ function testDrupalGetDestination() {
+ $query = $this->randomName(10);
+
+ // Verify that a 'destination' query string is used as destination.
+ $this->drupalGet('common-test/destination', array('query' => array('destination' => $query)));
+ $this->assertText('The destination: ' . $query, 'The given query string destination is determined as destination.');
+
+ // Verify that the current path is used as destination.
+ $this->drupalGet('common-test/destination', array('query' => array($query => NULL)));
+ $url = 'common-test/destination?' . $query;
+ $this->assertText('The destination: ' . $url, 'The current path is determined as destination.');
+ }
+}
+
+/**
+ * Tests for the JavaScript system.
+ */
+class JavaScriptTestCase extends DrupalWebTestCase {
+ /**
+ * Store configured value for JavaScript preprocessing.
+ */
+ protected $preprocess_js = NULL;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'JavaScript',
+ 'description' => 'Tests the JavaScript system.',
+ 'group' => 'System'
+ );
+ }
+
+ function setUp() {
+ // Enable Locale and SimpleTest in the test environment.
+ parent::setUp('locale', 'simpletest', 'common_test');
+
+ // Disable preprocessing
+ $this->preprocess_js = variable_get('preprocess_js', 0);
+ variable_set('preprocess_js', 0);
+
+ // Reset drupal_add_js() and drupal_add_library() statics before each test.
+ drupal_static_reset('drupal_add_js');
+ drupal_static_reset('drupal_add_library');
+ }
+
+ function tearDown() {
+ // Restore configured value for JavaScript preprocessing.
+ variable_set('preprocess_js', $this->preprocess_js);
+ parent::tearDown();
+ }
+
+ /**
+ * Test default JavaScript is empty.
+ */
+ function testDefault() {
+ $this->assertEqual(array(), drupal_add_js(), 'Default JavaScript is empty.');
+ }
+
+ /**
+ * Test adding a JavaScript file.
+ */
+ function testAddFile() {
+ $javascript = drupal_add_js('misc/collapse.js');
+ $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), 'jQuery is added when a file is added.');
+ $this->assertTrue(array_key_exists('misc/drupal.js', $javascript), 'Drupal.js is added when file is added.');
+ $this->assertTrue(array_key_exists('misc/collapse.js', $javascript), 'JavaScript files are correctly added.');
+ $this->assertEqual(base_path(), $javascript['settings']['data'][0]['basePath'], 'Base path JavaScript setting is correctly set.');
+ url('', array('prefix' => &$prefix));
+ $this->assertEqual(empty($prefix) ? '' : $prefix, $javascript['settings']['data'][1]['pathPrefix'], 'Path prefix JavaScript setting is correctly set.');
+ }
+
+ /**
+ * Test adding settings.
+ */
+ function testAddSetting() {
+ $javascript = drupal_add_js(array('drupal' => 'rocks', 'dries' => 280342800), 'setting');
+ $this->assertEqual(280342800, $javascript['settings']['data'][2]['dries'], 'JavaScript setting is set correctly.');
+ $this->assertEqual('rocks', $javascript['settings']['data'][2]['drupal'], 'The other JavaScript setting is set correctly.');
+ }
+
+ /**
+ * Tests adding an external JavaScript File.
+ */
+ function testAddExternal() {
+ $path = 'http://example.com/script.js';
+ $javascript = drupal_add_js($path, 'external');
+ $this->assertTrue(array_key_exists('http://example.com/script.js', $javascript), 'Added an external JavaScript file.');
+ }
+
+ /**
+ * Test drupal_get_js() for JavaScript settings.
+ */
+ function testHeaderSetting() {
+ // Only the second of these two entries should appear in Drupal.settings.
+ drupal_add_js(array('commonTest' => 'commonTestShouldNotAppear'), 'setting');
+ drupal_add_js(array('commonTest' => 'commonTestShouldAppear'), 'setting');
+ // All three of these entries should appear in Drupal.settings.
+ drupal_add_js(array('commonTestArray' => array('commonTestValue0')), 'setting');
+ drupal_add_js(array('commonTestArray' => array('commonTestValue1')), 'setting');
+ drupal_add_js(array('commonTestArray' => array('commonTestValue2')), 'setting');
+ // Only the second of these two entries should appear in Drupal.settings.
+ drupal_add_js(array('commonTestArray' => array('key' => 'commonTestOldValue')), 'setting');
+ drupal_add_js(array('commonTestArray' => array('key' => 'commonTestNewValue')), 'setting');
+
+ $javascript = drupal_get_js('header');
+ $this->assertTrue(strpos($javascript, 'basePath') > 0, 'Rendered JavaScript header returns basePath setting.');
+ $this->assertTrue(strpos($javascript, 'misc/jquery.js') > 0, 'Rendered JavaScript header includes jQuery.');
+ $this->assertTrue(strpos($javascript, 'pathPrefix') > 0, 'Rendered JavaScript header returns pathPrefix setting.');
+
+ // Test whether drupal_add_js can be used to override a previous setting.
+ $this->assertTrue(strpos($javascript, 'commonTestShouldAppear') > 0, 'Rendered JavaScript header returns custom setting.');
+ $this->assertTrue(strpos($javascript, 'commonTestShouldNotAppear') === FALSE, 'drupal_add_js() correctly overrides a custom setting.');
+
+ // Test whether drupal_add_js can be used to add numerically indexed values
+ // to an array.
+ $array_values_appear = strpos($javascript, 'commonTestValue0') > 0 && strpos($javascript, 'commonTestValue1') > 0 && strpos($javascript, 'commonTestValue2') > 0;
+ $this->assertTrue($array_values_appear, 'drupal_add_js() correctly adds settings to the end of an indexed array.');
+
+ // Test whether drupal_add_js can be used to override the entry for an
+ // existing key in an associative array.
+ $associative_array_override = strpos($javascript, 'commonTestNewValue') > 0 && strpos($javascript, 'commonTestOldValue') === FALSE;
+ $this->assertTrue($associative_array_override, 'drupal_add_js() correctly overrides settings within an associative array.');
+ }
+
+ /**
+ * Test to see if resetting the JavaScript empties the cache.
+ */
+ function testReset() {
+ drupal_add_js('misc/collapse.js');
+ drupal_static_reset('drupal_add_js');
+ $this->assertEqual(array(), drupal_add_js(), 'Resetting the JavaScript correctly empties the cache.');
+ }
+
+ /**
+ * Test adding inline scripts.
+ */
+ function testAddInline() {
+ $inline = 'jQuery(function () { });';
+ $javascript = drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
+ $this->assertTrue(array_key_exists('misc/jquery.js', $javascript), 'jQuery is added when inline scripts are added.');
+ $data = end($javascript);
+ $this->assertEqual($inline, $data['data'], 'Inline JavaScript is correctly added to the footer.');
+ }
+
+ /**
+ * Test rendering an external JavaScript file.
+ */
+ function testRenderExternal() {
+ $external = 'http://example.com/example.js';
+ drupal_add_js($external, 'external');
+ $javascript = drupal_get_js();
+ // Local files have a base_path() prefix, external files should not.
+ $this->assertTrue(strpos($javascript, 'src="' . $external) > 0, 'Rendering an external JavaScript file.');
+ }
+
+ /**
+ * Test drupal_get_js() with a footer scope.
+ */
+ function testFooterHTML() {
+ $inline = 'jQuery(function () { });';
+ drupal_add_js($inline, array('type' => 'inline', 'scope' => 'footer'));
+ $javascript = drupal_get_js('footer');
+ $this->assertTrue(strpos($javascript, $inline) > 0, 'Rendered JavaScript footer returns the inline code.');
+ }
+
+ /**
+ * Test drupal_add_js() sets preproccess to false when cache is set to false.
+ */
+ function testNoCache() {
+ $javascript = drupal_add_js('misc/collapse.js', array('cache' => FALSE));
+ $this->assertFalse($javascript['misc/collapse.js']['preprocess'], 'Setting cache to FALSE sets proprocess to FALSE when adding JavaScript.');
+ }
+
+ /**
+ * Test adding a JavaScript file with a different group.
+ */
+ function testDifferentGroup() {
+ $javascript = drupal_add_js('misc/collapse.js', array('group' => JS_THEME));
+ $this->assertEqual($javascript['misc/collapse.js']['group'], JS_THEME, 'Adding a JavaScript file with a different group caches the given group.');
+ }
+
+ /**
+ * Test adding a JavaScript file with a different weight.
+ */
+ function testDifferentWeight() {
+ $javascript = drupal_add_js('misc/collapse.js', array('weight' => 2));
+ $this->assertEqual($javascript['misc/collapse.js']['weight'], 2, 'Adding a JavaScript file with a different weight caches the given weight.');
+ }
+
+ /**
+ * Tests JavaScript aggregation when files are added to a different scope.
+ */
+ function testAggregationOrder() {
+ // Enable JavaScript aggregation.
+ variable_set('preprocess_js', 1);
+ drupal_static_reset('drupal_add_js');
+
+ // Add two JavaScript files to the current request and build the cache.
+ drupal_add_js('misc/ajax.js');
+ drupal_add_js('misc/autocomplete.js');
+
+ $js_items = drupal_add_js();
+ drupal_build_js_cache(array(
+ 'misc/ajax.js' => $js_items['misc/ajax.js'],
+ 'misc/autocomplete.js' => $js_items['misc/autocomplete.js']
+ ));
+
+ // Store the expected key for the first item in the cache.
+ $cache = array_keys(variable_get('drupal_js_cache_files', array()));
+ $expected_key = $cache[0];
+
+ // Reset variables and add a file in a different scope first.
+ variable_del('drupal_js_cache_files');
+ drupal_static_reset('drupal_add_js');
+ drupal_add_js('some/custom/javascript_file.js', array('scope' => 'footer'));
+ drupal_add_js('misc/ajax.js');
+ drupal_add_js('misc/autocomplete.js');
+
+ // Rebuild the cache.
+ $js_items = drupal_add_js();
+ drupal_build_js_cache(array(
+ 'misc/ajax.js' => $js_items['misc/ajax.js'],
+ 'misc/autocomplete.js' => $js_items['misc/autocomplete.js']
+ ));
+
+ // Compare the expected key for the first file to the current one.
+ $cache = array_keys(variable_get('drupal_js_cache_files', array()));
+ $key = $cache[0];
+ $this->assertEqual($key, $expected_key, 'JavaScript aggregation is not affected by ordering in different scopes.');
+ }
+
+ /**
+ * Test JavaScript ordering.
+ */
+ function testRenderOrder() {
+ // Add a bunch of JavaScript in strange ordering.
+ drupal_add_js('(function($){alert("Weight 5 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
+ drupal_add_js('(function($){alert("Weight 0 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
+ drupal_add_js('(function($){alert("Weight 0 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
+ drupal_add_js('(function($){alert("Weight -8 #1");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
+ drupal_add_js('(function($){alert("Weight -8 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
+ drupal_add_js('(function($){alert("Weight -8 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
+ drupal_add_js('http://example.com/example.js?Weight -5 #1', array('type' => 'external', 'scope' => 'footer', 'weight' => -5));
+ drupal_add_js('(function($){alert("Weight -8 #4");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => -8));
+ drupal_add_js('(function($){alert("Weight 5 #2");})(jQuery);', array('type' => 'inline', 'scope' => 'footer', 'weight' => 5));
+ drupal_add_js('(function($){alert("Weight 0 #3");})(jQuery);', array('type' => 'inline', 'scope' => 'footer'));
+
+ // Construct the expected result from the regex.
+ $expected = array(
+ "-8 #1",
+ "-8 #2",
+ "-8 #3",
+ "-8 #4",
+ "-5 #1", // The external script.
+ "0 #1",
+ "0 #2",
+ "0 #3",
+ "5 #1",
+ "5 #2",
+ );
+
+ // Retrieve the rendered JavaScript and test against the regex.
+ $js = drupal_get_js('footer');
+ $matches = array();
+ if (preg_match_all('/Weight\s([-0-9]+\s[#0-9]+)/', $js, $matches)) {
+ $result = $matches[1];
+ }
+ else {
+ $result = array();
+ }
+ $this->assertIdentical($result, $expected, 'JavaScript is added in the expected weight order.');
+ }
+
+ /**
+ * Test rendering the JavaScript with a file's weight above jQuery's.
+ */
+ function testRenderDifferentWeight() {
+ // JavaScript files are sorted first by group, then by the 'every_page'
+ // flag, then by weight (see drupal_sort_css_js()), so to test the effect of
+ // weight, we need the other two options to be the same.
+ drupal_add_js('misc/collapse.js', array('group' => JS_LIBRARY, 'every_page' => TRUE, 'weight' => -21));
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'misc/collapse.js') < strpos($javascript, 'misc/jquery.js'), 'Rendering a JavaScript file above jQuery.');
+ }
+
+ /**
+ * Test altering a JavaScript's weight via hook_js_alter().
+ *
+ * @see simpletest_js_alter()
+ */
+ function testAlter() {
+ // Add both tableselect.js and simpletest.js, with a larger weight on SimpleTest.
+ drupal_add_js('misc/tableselect.js');
+ drupal_add_js(drupal_get_path('module', 'simpletest') . '/simpletest.js', array('weight' => 9999));
+
+ // Render the JavaScript, testing if simpletest.js was altered to be before
+ // tableselect.js. See simpletest_js_alter() to see where this alteration
+ // takes place.
+ $javascript = drupal_get_js();
+ $this->assertTrue(strpos($javascript, 'simpletest.js') < strpos($javascript, 'misc/tableselect.js'), 'Altering JavaScript weight through the alter hook.');
+ }
+
+ /**
+ * Adds a library to the page and tests for both its JavaScript and its CSS.
+ */
+ function testLibraryRender() {
+ $result = drupal_add_library('system', 'farbtastic');
+ $this->assertTrue($result !== FALSE, 'Library was added without errors.');
+ $scripts = drupal_get_js();
+ $styles = drupal_get_css();
+ $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), 'JavaScript of library was added to the page.');
+ $this->assertTrue(strpos($styles, 'misc/farbtastic/farbtastic.css'), 'Stylesheet of library was added to the page.');
+ }
+
+ /**
+ * Adds a JavaScript library to the page and alters it.
+ *
+ * @see common_test_library_alter()
+ */
+ function testLibraryAlter() {
+ // Verify that common_test altered the title of Farbtastic.
+ $library = drupal_get_library('system', 'farbtastic');
+ $this->assertEqual($library['title'], 'Farbtastic: Altered Library', 'Registered libraries were altered.');
+
+ // common_test_library_alter() also added a dependency on jQuery Form.
+ drupal_add_library('system', 'farbtastic');
+ $scripts = drupal_get_js();
+ $this->assertTrue(strpos($scripts, 'misc/jquery.form.js'), 'Altered library dependencies are added to the page.');
+ }
+
+ /**
+ * Tests that multiple modules can implement the same library.
+ *
+ * @see common_test_library()
+ */
+ function testLibraryNameConflicts() {
+ $farbtastic = drupal_get_library('common_test', 'farbtastic');
+ $this->assertEqual($farbtastic['title'], 'Custom Farbtastic Library', 'Alternative libraries can be added to the page.');
+ }
+
+ /**
+ * Tests non-existing libraries.
+ */
+ function testLibraryUnknown() {
+ $result = drupal_get_library('unknown', 'unknown');
+ $this->assertFalse($result, 'Unknown library returned FALSE.');
+ drupal_static_reset('drupal_get_library');
+
+ $result = drupal_add_library('unknown', 'unknown');
+ $this->assertFalse($result, 'Unknown library returned FALSE.');
+ $scripts = drupal_get_js();
+ $this->assertTrue(strpos($scripts, 'unknown') === FALSE, 'Unknown library was not added to the page.');
+ }
+
+ /**
+ * Tests the addition of libraries through the #attached['library'] property.
+ */
+ function testAttachedLibrary() {
+ $element['#attached']['library'][] = array('system', 'farbtastic');
+ drupal_render($element);
+ $scripts = drupal_get_js();
+ $this->assertTrue(strpos($scripts, 'misc/farbtastic/farbtastic.js'), 'The attached_library property adds the additional libraries.');
+ }
+
+ /**
+ * Tests retrieval of libraries via drupal_get_library().
+ */
+ function testGetLibrary() {
+ // Retrieve all libraries registered by a module.
+ $libraries = drupal_get_library('common_test');
+ $this->assertTrue(isset($libraries['farbtastic']), 'Retrieved all module libraries.');
+ // Retrieve all libraries for a module not implementing hook_library().
+ // Note: This test installs Locale module.
+ $libraries = drupal_get_library('locale');
+ $this->assertEqual($libraries, array(), 'Retrieving libraries from a module not implementing hook_library() returns an emtpy array.');
+
+ // Retrieve a specific library by module and name.
+ $farbtastic = drupal_get_library('common_test', 'farbtastic');
+ $this->assertEqual($farbtastic['version'], '5.3', 'Retrieved a single library.');
+ // Retrieve a non-existing library by module and name.
+ $farbtastic = drupal_get_library('common_test', 'foo');
+ $this->assertIdentical($farbtastic, FALSE, 'Retrieving a non-existing library returns FALSE.');
+ }
+
+ /**
+ * Tests that the query string remains intact when adding JavaScript files
+ * that have query string parameters.
+ */
+ function testAddJsFileWithQueryString() {
+ $this->drupalGet('common-test/query-string');
+ $query_string = variable_get('css_js_query_string', '0');
+ $this->assertRaw(drupal_get_path('module', 'node') . '/node.js?' . $query_string, 'Query string was appended correctly to js.');
+ }
+}
+
+/**
+ * Tests for drupal_render().
+ */
+class DrupalRenderTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'drupal_render()',
+ 'description' => 'Performs functional tests on drupal_render().',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('common_test');
+ }
+
+ /**
+ * Tests the output drupal_render() for some elementary input values.
+ */
+ function testDrupalRenderBasics() {
+ $types = array(
+ array(
+ 'name' => 'null',
+ 'value' => NULL,
+ 'expected' => '',
+ ),
+ array(
+ 'name' => 'no value',
+ 'expected' => '',
+ ),
+ array(
+ 'name' => 'empty string',
+ 'value' => '',
+ 'expected' => '',
+ ),
+ array(
+ 'name' => 'no access',
+ 'value' => array(
+ '#markup' => 'foo',
+ '#access' => FALSE,
+ ),
+ 'expected' => '',
+ ),
+ array(
+ 'name' => 'previously printed',
+ 'value' => array(
+ '#markup' => 'foo',
+ '#printed' => TRUE,
+ ),
+ 'expected' => '',
+ ),
+ array(
+ 'name' => 'printed in prerender',
+ 'value' => array(
+ '#markup' => 'foo',
+ '#pre_render' => array('common_test_drupal_render_printing_pre_render'),
+ ),
+ 'expected' => '',
+ ),
+ array(
+ 'name' => 'basic renderable array',
+ 'value' => array('#markup' => 'foo'),
+ 'expected' => 'foo',
+ ),
+ );
+ foreach($types as $type) {
+ $this->assertIdentical(drupal_render($type['value']), $type['expected'], '"' . $type['name'] . '" input rendered correctly by drupal_render().');
+ }
+ }
+
+ /**
+ * Test sorting by weight.
+ */
+ function testDrupalRenderSorting() {
+ $first = $this->randomName();
+ $second = $this->randomName();
+ // Build an array with '#weight' set for each element.
+ $elements = array(
+ 'second' => array(
+ '#weight' => 10,
+ '#markup' => $second,
+ ),
+ 'first' => array(
+ '#weight' => 0,
+ '#markup' => $first,
+ ),
+ );
+ $output = drupal_render($elements);
+
+ // The lowest weight element should appear last in $output.
+ $this->assertTrue(strpos($output, $second) > strpos($output, $first), 'Elements were sorted correctly by weight.');
+
+ // Confirm that the $elements array has '#sorted' set to TRUE.
+ $this->assertTrue($elements['#sorted'], "'#sorted' => TRUE was added to the array");
+
+ // Pass $elements through element_children() and ensure it remains
+ // sorted in the correct order. drupal_render() will return an empty string
+ // if used on the same array in the same request.
+ $children = element_children($elements);
+ $this->assertTrue(array_shift($children) == 'first', 'Child found in the correct order.');
+ $this->assertTrue(array_shift($children) == 'second', 'Child found in the correct order.');
+
+
+ // The same array structure again, but with #sorted set to TRUE.
+ $elements = array(
+ 'second' => array(
+ '#weight' => 10,
+ '#markup' => $second,
+ ),
+ 'first' => array(
+ '#weight' => 0,
+ '#markup' => $first,
+ ),
+ '#sorted' => TRUE,
+ );
+ $output = drupal_render($elements);
+
+ // The elements should appear in output in the same order as the array.
+ $this->assertTrue(strpos($output, $second) < strpos($output, $first), 'Elements were not sorted.');
+ }
+
+ /**
+ * Test #attached functionality in children elements.
+ */
+ function testDrupalRenderChildrenAttached() {
+ // The cache system is turned off for POST requests.
+ $request_method = $_SERVER['REQUEST_METHOD'];
+ $_SERVER['REQUEST_METHOD'] = 'GET';
+
+ // Create an element with a child and subchild. Each element loads a
+ // different JavaScript file using #attached.
+ $parent_js = drupal_get_path('module', 'user') . '/user.js';
+ $child_js = drupal_get_path('module', 'forum') . '/forum.js';
+ $subchild_js = drupal_get_path('module', 'book') . '/book.js';
+ $element = array(
+ '#type' => 'fieldset',
+ '#cache' => array(
+ 'keys' => array('simpletest', 'drupal_render', 'children_attached'),
+ ),
+ '#attached' => array('js' => array($parent_js)),
+ '#title' => 'Parent',
+ );
+ $element['child'] = array(
+ '#type' => 'fieldset',
+ '#attached' => array('js' => array($child_js)),
+ '#title' => 'Child',
+ );
+ $element['child']['subchild'] = array(
+ '#attached' => array('js' => array($subchild_js)),
+ '#markup' => 'Subchild',
+ );
+
+ // Render the element and verify the presence of #attached JavaScript.
+ drupal_render($element);
+ $scripts = drupal_get_js();
+ $this->assertTrue(strpos($scripts, $parent_js), 'The element #attached JavaScript was included.');
+ $this->assertTrue(strpos($scripts, $child_js), 'The child #attached JavaScript was included.');
+ $this->assertTrue(strpos($scripts, $subchild_js), 'The subchild #attached JavaScript was included.');
+
+ // Load the element from cache and verify the presence of the #attached
+ // JavaScript.
+ drupal_static_reset('drupal_add_js');
+ $this->assertTrue(drupal_render_cache_get($element), 'The element was retrieved from cache.');
+ $scripts = drupal_get_js();
+ $this->assertTrue(strpos($scripts, $parent_js), 'The element #attached JavaScript was included when loading from cache.');
+ $this->assertTrue(strpos($scripts, $child_js), 'The child #attached JavaScript was included when loading from cache.');
+ $this->assertTrue(strpos($scripts, $subchild_js), 'The subchild #attached JavaScript was included when loading from cache.');
+
+ $_SERVER['REQUEST_METHOD'] = $request_method;
+ }
+
+ /**
+ * Test passing arguments to the theme function.
+ */
+ function testDrupalRenderThemeArguments() {
+ $element = array(
+ '#theme' => 'common_test_foo',
+ );
+ // Test that defaults work.
+ $this->assertEqual(drupal_render($element), 'foobar', 'Defaults work');
+ $element = array(
+ '#theme' => 'common_test_foo',
+ '#foo' => $this->randomName(),
+ '#bar' => $this->randomName(),
+ );
+ // Test that passing arguments to the theme function works.
+ $this->assertEqual(drupal_render($element), $element['#foo'] . $element['#bar'], 'Passing arguments to theme functions works');
+ }
+
+ /**
+ * Test rendering form elements without passing through form_builder().
+ */
+ function testDrupalRenderFormElements() {
+ // Define a series of form elements.
+ $element = array(
+ '#type' => 'button',
+ '#value' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'submit'));
+
+ $element = array(
+ '#type' => 'textfield',
+ '#title' => $this->randomName(),
+ '#value' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'text'));
+
+ $element = array(
+ '#type' => 'password',
+ '#title' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'password'));
+
+ $element = array(
+ '#type' => 'textarea',
+ '#title' => $this->randomName(),
+ '#value' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//textarea');
+
+ $element = array(
+ '#type' => 'radio',
+ '#title' => $this->randomName(),
+ '#value' => FALSE,
+ );
+ $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'radio'));
+
+ $element = array(
+ '#type' => 'checkbox',
+ '#title' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'checkbox'));
+
+ $element = array(
+ '#type' => 'select',
+ '#title' => $this->randomName(),
+ '#options' => array(
+ 0 => $this->randomName(),
+ 1 => $this->randomName(),
+ ),
+ );
+ $this->assertRenderedElement($element, '//select');
+
+ $element = array(
+ '#type' => 'file',
+ '#title' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'file'));
+
+ $element = array(
+ '#type' => 'item',
+ '#title' => $this->randomName(),
+ '#markup' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//div[contains(@class, :class) and contains(., :markup)]/label[contains(., :label)]', array(
+ ':class' => 'form-type-item',
+ ':markup' => $element['#markup'],
+ ':label' => $element['#title'],
+ ));
+
+ $element = array(
+ '#type' => 'hidden',
+ '#title' => $this->randomName(),
+ '#value' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//input[@type=:type]', array(':type' => 'hidden'));
+
+ $element = array(
+ '#type' => 'link',
+ '#title' => $this->randomName(),
+ '#href' => $this->randomName(),
+ '#options' => array(
+ 'absolute' => TRUE,
+ ),
+ );
+ $this->assertRenderedElement($element, '//a[@href=:href and contains(., :title)]', array(
+ ':href' => url($element['#href'], array('absolute' => TRUE)),
+ ':title' => $element['#title'],
+ ));
+
+ $element = array(
+ '#type' => 'fieldset',
+ '#title' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//fieldset/legend[contains(., :title)]', array(
+ ':title' => $element['#title'],
+ ));
+
+ $element['item'] = array(
+ '#type' => 'item',
+ '#title' => $this->randomName(),
+ '#markup' => $this->randomName(),
+ );
+ $this->assertRenderedElement($element, '//fieldset/div/div[contains(@class, :class) and contains(., :markup)]', array(
+ ':class' => 'form-type-item',
+ ':markup' => $element['item']['#markup'],
+ ));
+ }
+
+ protected function assertRenderedElement(array $element, $xpath, array $xpath_args = array()) {
+ $original_element = $element;
+ $this->drupalSetContent(drupal_render($element));
+ $this->verbose('<pre>' . check_plain(var_export($original_element, TRUE)) . '</pre>'
+ . '<pre>' . check_plain(var_export($element, TRUE)) . '</pre>'
+ . '<hr />' . $this->drupalGetContent()
+ );
+
+ // @see DrupalWebTestCase::xpath()
+ $xpath = $this->buildXPathQuery($xpath, $xpath_args);
+ $element += array('#value' => NULL);
+ $this->assertFieldByXPath($xpath, $element['#value'], format_string('#type @type was properly rendered.', array(
+ '@type' => var_export($element['#type'], TRUE),
+ )));
+ }
+
+ /**
+ * Tests caching of an empty render item.
+ */
+ function testDrupalRenderCache() {
+ // Force a request via GET.
+ $request_method = $_SERVER['REQUEST_METHOD'];
+ $_SERVER['REQUEST_METHOD'] = 'GET';
+ // Create an empty element.
+ $test_element = array(
+ '#cache' => array(
+ 'cid' => 'render_cache_test',
+ ),
+ '#markup' => '',
+ );
+
+ // Render the element and confirm that it goes through the rendering
+ // process (which will set $element['#printed']).
+ $element = $test_element;
+ drupal_render($element);
+ $this->assertTrue(isset($element['#printed']), 'No cache hit');
+
+ // Render the element again and confirm that it is retrieved from the cache
+ // instead (so $element['#printed'] will not be set).
+ $element = $test_element;
+ drupal_render($element);
+ $this->assertFalse(isset($element['#printed']), 'Cache hit');
+
+ // Restore the previous request method.
+ $_SERVER['REQUEST_METHOD'] = $request_method;
+ }
+}
+
+/**
+ * Test for valid_url().
+ */
+class ValidUrlTestCase extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Valid URL',
+ 'description' => "Performs tests on Drupal's valid URL function.",
+ 'group' => 'System'
+ );
+ }
+
+ /**
+ * Test valid absolute URLs.
+ */
+ function testValidAbsolute() {
+ $url_schemes = array('http', 'https', 'ftp');
+ $valid_absolute_urls = array(
+ 'example.com',
+ 'www.example.com',
+ 'ex-ample.com',
+ '3xampl3.com',
+ 'example.com/paren(the)sis',
+ 'example.com/index.html#pagetop',
+ 'example.com:8080',
+ 'subdomain.example.com',
+ 'example.com/index.php?q=node',
+ 'example.com/index.php?q=node&param=false',
+ 'user@www.example.com',
+ 'user:pass@www.example.com:8080/login.php?do=login&style=%23#pagetop',
+ '127.0.0.1',
+ 'example.org?',
+ 'john%20doe:secret:foo@example.org/',
+ 'example.org/~,$\'*;',
+ 'caf%C3%A9.example.org',
+ '[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:80/index.html',
+ );
+
+ foreach ($url_schemes as $scheme) {
+ foreach ($valid_absolute_urls as $url) {
+ $test_url = $scheme . '://' . $url;
+ $valid_url = valid_url($test_url, TRUE);
+ $this->assertTrue($valid_url, format_string('@url is a valid url.', array('@url' => $test_url)));
+ }
+ }
+ }
+
+ /**
+ * Test invalid absolute URLs.
+ */
+ function testInvalidAbsolute() {
+ $url_schemes = array('http', 'https', 'ftp');
+ $invalid_ablosule_urls = array(
+ '',
+ 'ex!ample.com',
+ 'ex%ample.com',
+ );
+
+ foreach ($url_schemes as $scheme) {
+ foreach ($invalid_ablosule_urls as $url) {
+ $test_url = $scheme . '://' . $url;
+ $valid_url = valid_url($test_url, TRUE);
+ $this->assertFalse($valid_url, format_string('@url is NOT a valid url.', array('@url' => $test_url)));
+ }
+ }
+ }
+
+ /**
+ * Test valid relative URLs.
+ */
+ function testValidRelative() {
+ $valid_relative_urls = array(
+ 'paren(the)sis',
+ 'index.html#pagetop',
+ 'index.php?q=node',
+ 'index.php?q=node&param=false',
+ 'login.php?do=login&style=%23#pagetop',
+ );
+
+ foreach (array('', '/') as $front) {
+ foreach ($valid_relative_urls as $url) {
+ $test_url = $front . $url;
+ $valid_url = valid_url($test_url);
+ $this->assertTrue($valid_url, format_string('@url is a valid url.', array('@url' => $test_url)));
+ }
+ }
+ }
+
+ /**
+ * Test invalid relative URLs.
+ */
+ function testInvalidRelative() {
+ $invalid_relative_urls = array(
+ 'ex^mple',
+ 'example<>',
+ 'ex%ample',
+ );
+
+ foreach (array('', '/') as $front) {
+ foreach ($invalid_relative_urls as $url) {
+ $test_url = $front . $url;
+ $valid_url = valid_url($test_url);
+ $this->assertFALSE($valid_url, format_string('@url is NOT a valid url.', array('@url' => $test_url)));
+ }
+ }
+ }
+}
+
+/**
+ * Tests for CRUD API functions.
+ */
+class DrupalDataApiTest extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Data API functions',
+ 'description' => 'Tests the performance of CRUD APIs.',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('database_test');
+ }
+
+ /**
+ * Test the drupal_write_record() API function.
+ */
+ function testDrupalWriteRecord() {
+ // Insert a record - no columns allow NULL values.
+ $person = new stdClass();
+ $person->name = 'John';
+ $person->unknown_column = 123;
+ $insert_result = drupal_write_record('test', $person);
+ $this->assertTrue($insert_result == SAVED_NEW, 'Correct value returned when a record is inserted with drupal_write_record() for a table with a single-field primary key.');
+ $this->assertTrue(isset($person->id), 'Primary key is set on record created with drupal_write_record().');
+ $this->assertIdentical($person->age, 0, 'Age field set to default value.');
+ $this->assertIdentical($person->job, 'Undefined', 'Job field set to default value.');
+
+ // Verify that the record was inserted.
+ $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical($result->name, 'John', 'Name field set.');
+ $this->assertIdentical($result->age, '0', 'Age field set to default value.');
+ $this->assertIdentical($result->job, 'Undefined', 'Job field set to default value.');
+ $this->assertFalse(isset($result->unknown_column), 'Unknown column was ignored.');
+
+ // Update the newly created record.
+ $person->name = 'Peter';
+ $person->age = 27;
+ $person->job = NULL;
+ $update_result = drupal_write_record('test', $person, array('id'));
+ $this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a record updated with drupal_write_record() for table with single-field primary key.');
+
+ // Verify that the record was updated.
+ $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical($result->name, 'Peter', 'Name field set.');
+ $this->assertIdentical($result->age, '27', 'Age field set.');
+ $this->assertIdentical($result->job, '', 'Job field set and cast to string.');
+
+ // Try to insert NULL in columns that does not allow this.
+ $person = new stdClass();
+ $person->name = 'Ringo';
+ $person->age = NULL;
+ $person->job = NULL;
+ $insert_result = drupal_write_record('test', $person);
+ $this->assertTrue(isset($person->id), 'Primary key is set on record created with drupal_write_record().');
+ $result = db_query("SELECT * FROM {test} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical($result->name, 'Ringo', 'Name field set.');
+ $this->assertIdentical($result->age, '0', 'Age field set.');
+ $this->assertIdentical($result->job, '', 'Job field set.');
+
+ // Insert a record - the "age" column allows NULL.
+ $person = new stdClass();
+ $person->name = 'Paul';
+ $person->age = NULL;
+ $insert_result = drupal_write_record('test_null', $person);
+ $this->assertTrue(isset($person->id), 'Primary key is set on record created with drupal_write_record().');
+ $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical($result->name, 'Paul', 'Name field set.');
+ $this->assertIdentical($result->age, NULL, 'Age field set.');
+
+ // Insert a record - do not specify the value of a column that allows NULL.
+ $person = new stdClass();
+ $person->name = 'Meredith';
+ $insert_result = drupal_write_record('test_null', $person);
+ $this->assertTrue(isset($person->id), 'Primary key is set on record created with drupal_write_record().');
+ $this->assertIdentical($person->age, 0, 'Age field set to default value.');
+ $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical($result->name, 'Meredith', 'Name field set.');
+ $this->assertIdentical($result->age, '0', 'Age field set to default value.');
+
+ // Update the newly created record.
+ $person->name = 'Mary';
+ $person->age = NULL;
+ $update_result = drupal_write_record('test_null', $person, array('id'));
+ $result = db_query("SELECT * FROM {test_null} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical($result->name, 'Mary', 'Name field set.');
+ $this->assertIdentical($result->age, NULL, 'Age field set.');
+
+ // Insert a record - the "data" column should be serialized.
+ $person = new stdClass();
+ $person->name = 'Dave';
+ $update_result = drupal_write_record('test_serialized', $person);
+ $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical($result->name, 'Dave', 'Name field set.');
+ $this->assertIdentical($result->info, NULL, 'Info field set.');
+
+ $person->info = array();
+ $update_result = drupal_write_record('test_serialized', $person, array('id'));
+ $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical(unserialize($result->info), array(), 'Info field updated.');
+
+ // Update the serialized record.
+ $data = array('foo' => 'bar', 1 => 2, 'empty' => '', 'null' => NULL);
+ $person->info = $data;
+ $update_result = drupal_write_record('test_serialized', $person, array('id'));
+ $result = db_query("SELECT * FROM {test_serialized} WHERE id = :id", array(':id' => $person->id))->fetchObject();
+ $this->assertIdentical(unserialize($result->info), $data, 'Info field updated.');
+
+ // Run an update query where no field values are changed. The database
+ // layer should return zero for number of affected rows, but
+ // db_write_record() should still return SAVED_UPDATED.
+ $update_result = drupal_write_record('test_null', $person, array('id'));
+ $this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a valid update is run without changing any values.');
+
+ // Insert an object record for a table with a multi-field primary key.
+ $node_access = new stdClass();
+ $node_access->nid = mt_rand();
+ $node_access->gid = mt_rand();
+ $node_access->realm = $this->randomName();
+ $insert_result = drupal_write_record('node_access', $node_access);
+ $this->assertTrue($insert_result == SAVED_NEW, 'Correct value returned when a record is inserted with drupal_write_record() for a table with a multi-field primary key.');
+
+ // Update the record.
+ $update_result = drupal_write_record('node_access', $node_access, array('nid', 'gid', 'realm'));
+ $this->assertTrue($update_result == SAVED_UPDATED, 'Correct value returned when a record is updated with drupal_write_record() for a table with a multi-field primary key.');
+ }
+
+}
+
+/**
+ * Tests Simpletest error and exception collector.
+ */
+class DrupalErrorCollectionUnitTest extends DrupalWebTestCase {
+
+ /**
+ * Errors triggered during the test.
+ *
+ * Errors are intercepted by the overriden implementation
+ * of DrupalWebTestCase::error below.
+ *
+ * @var Array
+ */
+ protected $collectedErrors = array();
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'SimpleTest error collector',
+ 'description' => 'Performs tests on the Simpletest error and exception collector.',
+ 'group' => 'SimpleTest',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('system_test', 'error_test');
+ }
+
+ /**
+ * Test that simpletest collects errors from the tested site.
+ */
+ function testErrorCollect() {
+ $this->collectedErrors = array();
+ $this->drupalGet('error-test/generate-warnings-with-report');
+ $this->assertEqual(count($this->collectedErrors), 3, 'Three errors were collected');
+
+ if (count($this->collectedErrors) == 3) {
+ $this->assertError($this->collectedErrors[0], 'Notice', 'error_test_generate_warnings()', 'error_test.module', 'Undefined variable: bananas');
+ $this->assertError($this->collectedErrors[1], 'Warning', 'error_test_generate_warnings()', 'error_test.module', 'Division by zero');
+ $this->assertError($this->collectedErrors[2], 'User warning', 'error_test_generate_warnings()', 'error_test.module', 'Drupal is awesome');
+ }
+ else {
+ // Give back the errors to the log report.
+ foreach ($this->collectedErrors as $error) {
+ parent::error($error['message'], $error['group'], $error['caller']);
+ }
+ }
+ }
+
+ /**
+ * Error handler that collects errors in an array.
+ *
+ * This test class is trying to verify that simpletest correctly sees errors
+ * and warnings. However, it can't generate errors and warnings that
+ * propagate up to the testing framework itself, or these tests would always
+ * fail. So, this special copy of error() doesn't propagate the errors up
+ * the class hierarchy. It just stuffs them into a protected collectedErrors
+ * array for various assertions to inspect.
+ */
+ protected function error($message = '', $group = 'Other', array $caller = NULL) {
+ // Due to a WTF elsewhere, simpletest treats debug() and verbose()
+ // messages as if they were an 'error'. But, we don't want to collect
+ // those here. This function just wants to collect the real errors (PHP
+ // notices, PHP fatal errors, etc.), and let all the 'errors' from the
+ // 'User notice' group bubble up to the parent classes to be handled (and
+ // eventually displayed) as normal.
+ if ($group == 'User notice') {
+ parent::error($message, $group, $caller);
+ }
+ // Everything else should be collected but not propagated.
+ else {
+ $this->collectedErrors[] = array(
+ 'message' => $message,
+ 'group' => $group,
+ 'caller' => $caller
+ );
+ }
+ }
+
+ /**
+ * Assert that a collected error matches what we are expecting.
+ */
+ function assertError($error, $group, $function, $file, $message = NULL) {
+ $this->assertEqual($error['group'], $group, format_string("Group was %group", array('%group' => $group)));
+ $this->assertEqual($error['caller']['function'], $function, format_string("Function was %function", array('%function' => $function)));
+ $this->assertEqual(drupal_basename($error['caller']['file']), $file, format_string("File was %file", array('%file' => $file)));
+ if (isset($message)) {
+ $this->assertEqual($error['message'], $message, format_string("Message was %message", array('%message' => $message)));
+ }
+ }
+}
+
+/**
+ * Test the drupal_parse_info_file() API function.
+ */
+class ParseInfoFilesTestCase extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Parsing .info files',
+ 'description' => 'Tests parsing .info files.',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Parse an example .info file an verify the results.
+ */
+ function testParseInfoFile() {
+ $info_values = drupal_parse_info_file(drupal_get_path('module', 'simpletest') . '/tests/common_test_info.txt');
+ $this->assertEqual($info_values['simple_string'], 'A simple string', 'Simple string value was parsed correctly.', 'System');
+ $this->assertEqual($info_values['simple_constant'], WATCHDOG_INFO, 'Constant value was parsed correctly.', 'System');
+ $this->assertEqual($info_values['double_colon'], 'dummyClassName::', 'Value containing double-colon was parsed correctly.', 'System');
+ }
+}
+
+/**
+ * Tests for the drupal_system_listing() function.
+ */
+class DrupalSystemListingTestCase extends DrupalWebTestCase {
+ /**
+ * Use the testing profile; this is needed for testDirectoryPrecedence().
+ */
+ protected $profile = 'testing';
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Drupal system listing',
+ 'description' => 'Tests the mechanism for scanning system directories in drupal_system_listing().',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Test that files in different directories take precedence as expected.
+ */
+ function testDirectoryPrecedence() {
+ // Define the module files we will search for, and the directory precedence
+ // we expect.
+ $expected_directories = array(
+ // When the copy of the module in the profile directory is incompatible
+ // with Drupal core, the copy in the core modules directory takes
+ // precedence.
+ 'drupal_system_listing_incompatible_test' => array(
+ 'modules/simpletest/tests',
+ 'profiles/testing/modules',
+ ),
+ // When both copies of the module are compatible with Drupal core, the
+ // copy in the profile directory takes precedence.
+ 'drupal_system_listing_compatible_test' => array(
+ 'profiles/testing/modules',
+ 'modules/simpletest/tests',
+ ),
+ );
+
+ // This test relies on two versions of the same module existing in
+ // different places in the filesystem. Without that, the test has no
+ // meaning, so assert their presence first.
+ foreach ($expected_directories as $module => $directories) {
+ foreach ($directories as $directory) {
+ $filename = "$directory/$module/$module.module";
+ $this->assertTrue(file_exists(DRUPAL_ROOT . '/' . $filename), format_string('@filename exists.', array('@filename' => $filename)));
+ }
+ }
+
+ // Now scan the directories and check that the files take precedence as
+ // expected.
+ $files = drupal_system_listing('/\.module$/', 'modules', 'name', 1);
+ foreach ($expected_directories as $module => $directories) {
+ $expected_directory = array_shift($directories);
+ $expected_filename = "$expected_directory/$module/$module.module";
+ $this->assertEqual($files[$module]->uri, $expected_filename, format_string('Module @module was found at @filename.', array('@module' => $module, '@filename' => $expected_filename)));
+ }
+ }
+}
+
+/**
+ * Tests for the format_date() function.
+ */
+class FormatDateUnitTest extends DrupalWebTestCase {
+
+ /**
+ * Arbitrary langcode for a custom language.
+ */
+ const LANGCODE = 'xx';
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Format date',
+ 'description' => 'Test the format_date() function.',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('locale');
+ variable_set('configurable_timezones', 1);
+ variable_set('date_format_long', 'l, j. F Y - G:i');
+ variable_set('date_format_medium', 'j. F Y - G:i');
+ variable_set('date_format_short', 'Y M j - g:ia');
+ variable_set('locale_custom_strings_' . self::LANGCODE, array(
+ '' => array('Sunday' => 'domingo'),
+ 'Long month name' => array('March' => 'marzo'),
+ ));
+ $this->refreshVariables();
+ }
+
+ /**
+ * Test admin-defined formats in format_date().
+ */
+ function testAdminDefinedFormatDate() {
+ // Create an admin user.
+ $this->admin_user = $this->drupalCreateUser(array('administer site configuration'));
+ $this->drupalLogin($this->admin_user);
+
+ // Add new date format.
+ $admin_date_format = 'j M y';
+ $edit = array('date_format' => $admin_date_format);
+ $this->drupalPost('admin/config/regional/date-time/formats/add', $edit, 'Add format');
+
+ // Add a new date format which just differs in the case.
+ $admin_date_format_uppercase = 'j M Y';
+ $edit = array('date_format' => $admin_date_format_uppercase);
+ $this->drupalPost('admin/config/regional/date-time/formats/add', $edit, t('Add format'));
+ $this->assertText(t('Custom date format added.'));
+
+ // Add new date type.
+ $edit = array(
+ 'date_type' => 'Example Style',
+ 'machine_name' => 'example_style',
+ 'date_format' => $admin_date_format,
+ );
+ $this->drupalPost('admin/config/regional/date-time/types/add', $edit, 'Add date type');
+
+ // Add a second date format with a different case than the first.
+ $edit = array(
+ 'machine_name' => 'example_style_uppercase',
+ 'date_type' => 'Example Style Uppercase',
+ 'date_format' => $admin_date_format_uppercase,
+ );
+ $this->drupalPost('admin/config/regional/date-time/types/add', $edit, t('Add date type'));
+ $this->assertText(t('New date type added successfully.'));
+
+ $timestamp = strtotime('2007-03-10T00:00:00+00:00');
+ $this->assertIdentical(format_date($timestamp, 'example_style', '', 'America/Los_Angeles'), '9 Mar 07', 'Test format_date() using an admin-defined date type.');
+ $this->assertIdentical(format_date($timestamp, 'example_style_uppercase', '', 'America/Los_Angeles'), '9 Mar 2007', 'Test format_date() using an admin-defined date type with different case.');
+ $this->assertIdentical(format_date($timestamp, 'undefined_style'), format_date($timestamp, 'medium'), 'Test format_date() defaulting to medium when $type not found.');
+ }
+
+ /**
+ * Tests for the format_date() function.
+ */
+ function testFormatDate() {
+ global $user, $language;
+
+ $timestamp = strtotime('2007-03-26T00:00:00+00:00');
+ $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', 'Test all parameters.');
+ $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'domingo, 25-Mar-07 17:00:00 PDT', 'Test translated format.');
+ $this->assertIdentical(format_date($timestamp, 'custom', '\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), 'l, 25-Mar-07 17:00:00 PDT', 'Test an escaped format string.');
+ $this->assertIdentical(format_date($timestamp, 'custom', '\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\domingo, 25-Mar-07 17:00:00 PDT', 'Test format containing backslash character.');
+ $this->assertIdentical(format_date($timestamp, 'custom', '\\\\\\l, d-M-y H:i:s T', 'America/Los_Angeles', self::LANGCODE), '\\l, 25-Mar-07 17:00:00 PDT', 'Test format containing backslash followed by escaped format string.');
+ $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London', 'en'), 'Monday, 26-Mar-07 01:00:00 BST', 'Test a different time zone.');
+
+ // Create an admin user and add Spanish language.
+ $admin_user = $this->drupalCreateUser(array('administer languages'));
+ $this->drupalLogin($admin_user);
+ $edit = array(
+ 'langcode' => self::LANGCODE,
+ 'name' => self::LANGCODE,
+ 'native' => self::LANGCODE,
+ 'direction' => LANGUAGE_LTR,
+ 'prefix' => self::LANGCODE,
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+
+ // Create a test user to carry out the tests.
+ $test_user = $this->drupalCreateUser();
+ $this->drupalLogin($test_user);
+ $edit = array('language' => self::LANGCODE, 'mail' => $test_user->mail, 'timezone' => 'America/Los_Angeles');
+ $this->drupalPost('user/' . $test_user->uid . '/edit', $edit, t('Save'));
+
+ // Disable session saving as we are about to modify the global $user.
+ drupal_save_session(FALSE);
+ // Save the original user and language and then replace it with the test user and language.
+ $real_user = $user;
+ $user = user_load($test_user->uid, TRUE);
+ $real_language = $language->language;
+ $language->language = $user->language;
+ // Simulate a Drupal bootstrap with the logged-in user.
+ date_default_timezone_set(drupal_get_user_timezone());
+
+ $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'America/Los_Angeles', 'en'), 'Sunday, 25-Mar-07 17:00:00 PDT', 'Test a different language.');
+ $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T', 'Europe/London'), 'Monday, 26-Mar-07 01:00:00 BST', 'Test a different time zone.');
+ $this->assertIdentical(format_date($timestamp, 'custom', 'l, d-M-y H:i:s T'), 'domingo, 25-Mar-07 17:00:00 PDT', 'Test custom date format.');
+ $this->assertIdentical(format_date($timestamp, 'long'), 'domingo, 25. marzo 2007 - 17:00', 'Test long date format.');
+ $this->assertIdentical(format_date($timestamp, 'medium'), '25. marzo 2007 - 17:00', 'Test medium date format.');
+ $this->assertIdentical(format_date($timestamp, 'short'), '2007 Mar 25 - 5:00pm', 'Test short date format.');
+ $this->assertIdentical(format_date($timestamp), '25. marzo 2007 - 17:00', 'Test default date format.');
+
+ // Restore the original user and language, and enable session saving.
+ $user = $real_user;
+ $language->language = $real_language;
+ // Restore default time zone.
+ date_default_timezone_set(drupal_get_user_timezone());
+ drupal_save_session(TRUE);
+ }
+}
+
+/**
+ * Tests for the format_date() function.
+ */
+class DrupalAttributesUnitTest extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'HTML Attributes',
+ 'description' => 'Perform unit tests on the drupal_attributes() function.',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Tests that drupal_html_class() cleans the class name properly.
+ */
+ function testDrupalAttributes() {
+ // Verify that special characters are HTML encoded.
+ $this->assertIdentical(drupal_attributes(array('title' => '&"\'<>')), ' title="&amp;&quot;&#039;&lt;&gt;"', 'HTML encode attribute values.');
+
+ // Verify multi-value attributes are concatenated with spaces.
+ $attributes = array('class' => array('first', 'last'));
+ $this->assertIdentical(drupal_attributes(array('class' => array('first', 'last'))), ' class="first last"', 'Concatenate multi-value attributes.');
+
+ // Verify empty attribute values are rendered.
+ $this->assertIdentical(drupal_attributes(array('alt' => '')), ' alt=""', 'Empty attribute value #1.');
+ $this->assertIdentical(drupal_attributes(array('alt' => NULL)), ' alt=""', 'Empty attribute value #2.');
+
+ // Verify multiple attributes are rendered.
+ $attributes = array(
+ 'id' => 'id-test',
+ 'class' => array('first', 'last'),
+ 'alt' => 'Alternate',
+ );
+ $this->assertIdentical(drupal_attributes($attributes), ' id="id-test" class="first last" alt="Alternate"', 'Multiple attributes.');
+
+ // Verify empty attributes array is rendered.
+ $this->assertIdentical(drupal_attributes(array()), '', 'Empty attributes array.');
+ }
+}
+
+/**
+ * Tests converting PHP variables to JSON strings and back.
+ */
+class DrupalJSONTest extends DrupalUnitTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'JSON',
+ 'description' => 'Perform unit tests on the drupal_json_encode() and drupal_json_decode() functions.',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Tests converting PHP variables to JSON strings and back.
+ */
+ function testJSON() {
+ // Setup a string with the full ASCII table.
+ // @todo: Add tests for non-ASCII characters and Unicode.
+ $str = '';
+ for ($i=0; $i < 128; $i++) {
+ $str .= chr($i);
+ }
+ // Characters that must be escaped.
+ // We check for unescaped " separately.
+ $html_unsafe = array('<', '>', '\'', '&');
+ // The following are the encoded forms of: < > ' & "
+ $html_unsafe_escaped = array('\u003C', '\u003E', '\u0027', '\u0026', '\u0022');
+
+ // Verify there aren't character encoding problems with the source string.
+ $this->assertIdentical(strlen($str), 128, 'A string with the full ASCII table has the correct length.');
+ foreach ($html_unsafe as $char) {
+ $this->assertTrue(strpos($str, $char) > 0, format_string('A string with the full ASCII table includes @s.', array('@s' => $char)));
+ }
+
+ // Verify that JSON encoding produces a string with all of the characters.
+ $json = drupal_json_encode($str);
+ $this->assertTrue(strlen($json) > strlen($str), 'A JSON encoded string is larger than the source string.');
+
+ // The first and last characters should be ", and no others.
+ $this->assertTrue($json[0] == '"', 'A JSON encoded string begins with ".');
+ $this->assertTrue($json[strlen($json) - 1] == '"', 'A JSON encoded string ends with ".');
+ $this->assertTrue(substr_count($json, '"') == 2, 'A JSON encoded string contains exactly two ".');
+
+ // Verify that encoding/decoding is reversible.
+ $json_decoded = drupal_json_decode($json);
+ $this->assertIdentical($str, $json_decoded, 'Encoding a string to JSON and decoding back results in the original string.');
+
+ // Verify reversibility for structured data. Also verify that necessary
+ // characters are escaped.
+ $source = array(TRUE, FALSE, 0, 1, '0', '1', $str, array('key1' => $str, 'key2' => array('nested' => TRUE)));
+ $json = drupal_json_encode($source);
+ foreach ($html_unsafe as $char) {
+ $this->assertTrue(strpos($json, $char) === FALSE, format_string('A JSON encoded string does not contain @s.', array('@s' => $char)));
+ }
+ // Verify that JSON encoding escapes the HTML unsafe characters
+ foreach ($html_unsafe_escaped as $char) {
+ $this->assertTrue(strpos($json, $char) > 0, format_string('A JSON encoded string contains @s.', array('@s' => $char)));
+ }
+ $json_decoded = drupal_json_decode($json);
+ $this->assertNotIdentical($source, $json, 'An array encoded in JSON is not identical to the source.');
+ $this->assertIdentical($source, $json_decoded, 'Encoding structured data to JSON and decoding back results in the original data.');
+ }
+}
+
+/**
+ * Tests for RDF namespaces XML serialization.
+ */
+class DrupalGetRdfNamespacesTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'RDF namespaces XML serialization tests',
+ 'description' => 'Confirm that the serialization of RDF namespaces via drupal_get_rdf_namespaces() is output and parsed correctly in the XHTML document.',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('rdf', 'rdf_test');
+ }
+
+ /**
+ * Test RDF namespaces.
+ */
+ function testGetRdfNamespaces() {
+ // Fetches the front page and extracts XML namespaces.
+ $this->drupalGet('');
+ $xml = new SimpleXMLElement($this->content);
+ $ns = $xml->getDocNamespaces();
+
+ $this->assertEqual($ns['rdfs'], 'http://www.w3.org/2000/01/rdf-schema#', 'A prefix declared once is displayed.');
+ $this->assertEqual($ns['foaf'], 'http://xmlns.com/foaf/0.1/', 'The same prefix declared in several implementations of hook_rdf_namespaces() is valid as long as all the namespaces are the same.');
+ $this->assertEqual($ns['foaf1'], 'http://xmlns.com/foaf/0.1/', 'Two prefixes can be assigned the same namespace.');
+ $this->assertTrue(!isset($ns['dc']), 'A prefix with conflicting namespaces is discarded.');
+ }
+}
+
+/**
+ * Basic tests for drupal_add_feed().
+ */
+class DrupalAddFeedTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'drupal_add_feed() tests',
+ 'description' => 'Make sure that drupal_add_feed() works correctly with various constructs.',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Test drupal_add_feed() with paths, URLs, and titles.
+ */
+ function testBasicFeedAddNoTitle() {
+ $path = $this->randomName(12);
+ $external_url = 'http://' . $this->randomName(12) . '/' . $this->randomName(12);
+ $fully_qualified_local_url = url($this->randomName(12), array('absolute' => TRUE));
+
+ $path_for_title = $this->randomName(12);
+ $external_for_title = 'http://' . $this->randomName(12) . '/' . $this->randomName(12);
+ $fully_qualified_for_title = url($this->randomName(12), array('absolute' => TRUE));
+
+ // Possible permutations of drupal_add_feed() to test.
+ // - 'input_url': the path passed to drupal_add_feed(),
+ // - 'output_url': the expected URL to be found in the header.
+ // - 'title' == the title of the feed as passed into drupal_add_feed().
+ $urls = array(
+ 'path without title' => array(
+ 'input_url' => $path,
+ 'output_url' => url($path, array('absolute' => TRUE)),
+ 'title' => '',
+ ),
+ 'external URL without title' => array(
+ 'input_url' => $external_url,
+ 'output_url' => $external_url,
+ 'title' => '',
+ ),
+ 'local URL without title' => array(
+ 'input_url' => $fully_qualified_local_url,
+ 'output_url' => $fully_qualified_local_url,
+ 'title' => '',
+ ),
+ 'path with title' => array(
+ 'input_url' => $path_for_title,
+ 'output_url' => url($path_for_title, array('absolute' => TRUE)),
+ 'title' => $this->randomName(12),
+ ),
+ 'external URL with title' => array(
+ 'input_url' => $external_for_title,
+ 'output_url' => $external_for_title,
+ 'title' => $this->randomName(12),
+ ),
+ 'local URL with title' => array(
+ 'input_url' => $fully_qualified_for_title,
+ 'output_url' => $fully_qualified_for_title,
+ 'title' => $this->randomName(12),
+ ),
+ );
+
+ foreach ($urls as $description => $feed_info) {
+ drupal_add_feed($feed_info['input_url'], $feed_info['title']);
+ }
+
+ $this->drupalSetContent(drupal_get_html_head());
+ foreach ($urls as $description => $feed_info) {
+ $this->assertPattern($this->urlToRSSLinkPattern($feed_info['output_url'], $feed_info['title']), format_string('Found correct feed header for %description', array('%description' => $description)));
+ }
+ }
+
+ /**
+ * Create a pattern representing the RSS feed in the page.
+ */
+ function urlToRSSLinkPattern($url, $title = '') {
+ // Escape any regular expression characters in the URL ('?' is the worst).
+ $url = preg_replace('/([+?.*])/', '[$0]', $url);
+ $generated_pattern = '%<link +rel="alternate" +type="application/rss.xml" +title="' . $title . '" +href="' . $url . '" */>%';
+ return $generated_pattern;
+ }
+}
+
+/**
+ * Test for theme_feed_icon().
+ */
+class FeedIconTest extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Feed icon',
+ 'description' => 'Check escaping of theme_feed_icon()',
+ 'group' => 'System',
+ );
+ }
+
+ /**
+ * Check that special characters are correctly escaped. Test for issue #1211668.
+ */
+ function testFeedIconEscaping() {
+ $variables = array();
+ $variables['url'] = 'node';
+ $variables['title'] = '<>&"\'';
+ $text = theme_feed_icon($variables);
+ preg_match('/title="(.*?)"/', $text, $matches);
+ $this->assertEqual($matches[1], 'Subscribe to &amp;&quot;&#039;', 'theme_feed_icon() escapes reserved HTML characters.');
+ }
+
+}
+
+/**
+ * Test array diff functions.
+ */
+class ArrayDiffUnitTest extends DrupalUnitTestCase {
+
+ /**
+ * Array to use for testing.
+ *
+ * @var array
+ */
+ protected $array1;
+
+ /**
+ * Array to use for testing.
+ *
+ * @var array
+ */
+ protected $array2;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Array differences',
+ 'description' => 'Performs tests on drupal_array_diff_assoc_recursive().',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ $this->array1 = array(
+ 'same' => 'yes',
+ 'different' => 'no',
+ 'array_empty_diff' => array(),
+ 'null' => NULL,
+ 'int_diff' => 1,
+ 'array_diff' => array('same' => 'same', 'array' => array('same' => 'same')),
+ 'array_compared_to_string' => array('value'),
+ 'string_compared_to_array' => 'value',
+ 'new' => 'new',
+ );
+ $this->array2 = array(
+ 'same' => 'yes',
+ 'different' => 'yes',
+ 'array_empty_diff' => array(),
+ 'null' => NULL,
+ 'int_diff' => '1',
+ 'array_diff' => array('same' => 'different', 'array' => array('same' => 'same')),
+ 'array_compared_to_string' => 'value',
+ 'string_compared_to_array' => array('value'),
+ );
+ }
+
+
+ /**
+ * Tests drupal_array_diff_assoc_recursive().
+ */
+ public function testArrayDiffAssocRecursive() {
+ $expected = array(
+ 'different' => 'no',
+ 'int_diff' => 1,
+ // The 'array' key should not be returned, as it's the same.
+ 'array_diff' => array('same' => 'same'),
+ 'array_compared_to_string' => array('value'),
+ 'string_compared_to_array' => 'value',
+ 'new' => 'new',
+ );
+
+ $this->assertIdentical(drupal_array_diff_assoc_recursive($this->array1, $this->array2), $expected);
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.css b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.css
new file mode 100644
index 0000000..b86cead
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.css
@@ -0,0 +1,2 @@
+
+/* This file is for testing CSS file inclusion, no contents are necessary. */
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.info
new file mode 100644
index 0000000..e44c56e
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.info
@@ -0,0 +1,14 @@
+name = "Common Test"
+description = "Support module for Common tests."
+package = Testing
+version = VERSION
+core = 7.x
+stylesheets[all][] = common_test.css
+stylesheets[print][] = common_test.print.css
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.module
new file mode 100644
index 0000000..674a494
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.module
@@ -0,0 +1,276 @@
+<?php
+
+/**
+ * @file
+ * Helper module for the Common tests.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function common_test_menu() {
+ $items['common-test/drupal_goto'] = array(
+ 'title' => 'Drupal Goto',
+ 'page callback' => 'common_test_drupal_goto_land',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['common-test/drupal_goto/fail'] = array(
+ 'title' => 'Drupal Goto',
+ 'page callback' => 'common_test_drupal_goto_land_fail',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['common-test/drupal_goto/redirect'] = array(
+ 'title' => 'Drupal Goto',
+ 'page callback' => 'common_test_drupal_goto_redirect',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['common-test/drupal_goto/redirect_advanced'] = array(
+ 'title' => 'Drupal Goto',
+ 'page callback' => 'common_test_drupal_goto_redirect_advanced',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['common-test/drupal_goto/redirect_fail'] = array(
+ 'title' => 'Drupal Goto Failure',
+ 'page callback' => 'drupal_goto',
+ 'page arguments' => array('common-test/drupal_goto/fail'),
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['common-test/destination'] = array(
+ 'title' => 'Drupal Get Destination',
+ 'page callback' => 'common_test_destination',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['common-test/query-string'] = array(
+ 'title' => 'Test querystring',
+ 'page callback' => 'common_test_js_and_css_querystring',
+ 'access arguments' => array('access content'),
+ 'type' => MENU_CALLBACK,
+ );
+ return $items;
+}
+
+/**
+ * Redirect using drupal_goto().
+ */
+function common_test_drupal_goto_redirect() {
+ drupal_goto('common-test/drupal_goto');
+}
+
+/**
+ * Redirect using drupal_goto().
+ */
+function common_test_drupal_goto_redirect_advanced() {
+ drupal_goto('common-test/drupal_goto', array('query' => array('foo' => '123')), 301);
+}
+
+/**
+ * Landing page for drupal_goto().
+ */
+function common_test_drupal_goto_land() {
+ print "drupal_goto";
+}
+
+/**
+ * Fail landing page for drupal_goto().
+ */
+function common_test_drupal_goto_land_fail() {
+ print "drupal_goto_fail";
+}
+
+/**
+ * Implements hook_drupal_goto_alter().
+ */
+function common_test_drupal_goto_alter(&$path, &$options, &$http_response_code) {
+ if ($path == 'common-test/drupal_goto/fail') {
+ $path = 'common-test/drupal_goto/redirect';
+ }
+}
+
+/**
+ * Print destination query parameter.
+ */
+function common_test_destination() {
+ $destination = drupal_get_destination();
+ print "The destination: " . check_plain($destination['destination']);
+}
+
+/**
+ * Applies #printed to an element to help test #pre_render.
+ */
+function common_test_drupal_render_printing_pre_render($elements) {
+ $elements['#printed'] = TRUE;
+ return $elements;
+}
+
+/**
+ * Implements hook_TYPE_alter().
+ */
+function common_test_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
+ // Alter first argument.
+ if (is_array($data)) {
+ $data['foo'] = 'Drupal';
+ }
+ elseif (is_object($data)) {
+ $data->foo = 'Drupal';
+ }
+ // Alter second argument, if present.
+ if (isset($arg2)) {
+ if (is_array($arg2)) {
+ $arg2['foo'] = 'Drupal';
+ }
+ elseif (is_object($arg2)) {
+ $arg2->foo = 'Drupal';
+ }
+ }
+ // Try to alter third argument, if present.
+ if (isset($arg3)) {
+ if (is_array($arg3)) {
+ $arg3['foo'] = 'Drupal';
+ }
+ elseif (is_object($arg3)) {
+ $arg3->foo = 'Drupal';
+ }
+ }
+}
+
+/**
+ * Implements hook_TYPE_alter() on behalf of Bartik theme.
+ *
+ * Same as common_test_drupal_alter_alter(), but here, we verify that themes
+ * can also alter and come last.
+ */
+function bartik_drupal_alter_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
+ // Alter first argument.
+ if (is_array($data)) {
+ $data['foo'] .= ' theme';
+ }
+ elseif (is_object($data)) {
+ $data->foo .= ' theme';
+ }
+ // Alter second argument, if present.
+ if (isset($arg2)) {
+ if (is_array($arg2)) {
+ $arg2['foo'] .= ' theme';
+ }
+ elseif (is_object($arg2)) {
+ $arg2->foo .= ' theme';
+ }
+ }
+ // Try to alter third argument, if present.
+ if (isset($arg3)) {
+ if (is_array($arg3)) {
+ $arg3['foo'] .= ' theme';
+ }
+ elseif (is_object($arg3)) {
+ $arg3->foo .= ' theme';
+ }
+ }
+}
+
+/**
+ * Implements hook_TYPE_alter() on behalf of block module.
+ *
+ * This is for verifying that drupal_alter(array(TYPE1, TYPE2), ...) allows
+ * hook_module_implements_alter() to affect the order in which module
+ * implementations are executed.
+ */
+function block_drupal_alter_foo_alter(&$data, &$arg2 = NULL, &$arg3 = NULL) {
+ $data['foo'] .= ' block';
+}
+
+/**
+ * Implements hook_module_implements_alter().
+ *
+ * @see block_drupal_alter_foo_alter()
+ */
+function common_test_module_implements_alter(&$implementations, $hook) {
+ // For drupal_alter(array('drupal_alter', 'drupal_alter_foo'), ...), make the
+ // block module implementations run after all the other modules. Note that
+ // when drupal_alter() is called with an array of types, the first type is
+ // considered primary and controls the module order.
+ if ($hook == 'drupal_alter_alter' && isset($implementations['block'])) {
+ $group = $implementations['block'];
+ unset($implementations['block']);
+ $implementations['block'] = $group;
+ }
+}
+
+/**
+ * Implements hook_theme().
+ */
+function common_test_theme() {
+ return array(
+ 'common_test_foo' => array(
+ 'variables' => array('foo' => 'foo', 'bar' => 'bar'),
+ ),
+ );
+}
+
+/**
+ * Theme function for testing drupal_render() theming.
+ */
+function theme_common_test_foo($variables) {
+ return $variables['foo'] . $variables['bar'];
+}
+
+/**
+ * Implements hook_library_alter().
+ */
+function common_test_library_alter(&$libraries, $module) {
+ if ($module == 'system' && isset($libraries['farbtastic'])) {
+ // Change the title of Farbtastic to "Farbtastic: Altered Library".
+ $libraries['farbtastic']['title'] = 'Farbtastic: Altered Library';
+ // Make Farbtastic depend on jQuery Form to test library dependencies.
+ $libraries['farbtastic']['dependencies'][] = array('system', 'form');
+ }
+}
+
+/**
+ * Implements hook_library().
+ *
+ * Adds Farbtastic in a different version.
+ */
+function common_test_library() {
+ $libraries['farbtastic'] = array(
+ 'title' => 'Custom Farbtastic Library',
+ 'website' => 'http://code.google.com/p/farbtastic/',
+ 'version' => '5.3',
+ 'js' => array(
+ 'misc/farbtastic/farbtastic.js' => array(),
+ ),
+ 'css' => array(
+ 'misc/farbtastic/farbtastic.css' => array(),
+ ),
+ );
+ return $libraries;
+}
+
+/**
+ * Adds a JavaScript file and a CSS file with a query string appended.
+ */
+function common_test_js_and_css_querystring() {
+ drupal_add_js(drupal_get_path('module', 'node') . '/node.js');
+ drupal_add_css(drupal_get_path('module', 'node') . '/node.css');
+ // A relative URI may have a query string.
+ drupal_add_css('/' . drupal_get_path('module', 'node') . '/node-fake.css?arg1=value1&arg2=value2');
+ return '';
+}
+
+/**
+ * Implements hook_cron().
+ *
+ * System module should handle if a module does not catch an exception and keep
+ * cron going.
+ *
+ * @see common_test_cron_helper()
+ *
+ */
+function common_test_cron() {
+ throw new Exception(t('Uncaught exception'));
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.print.css b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.print.css
new file mode 100644
index 0000000..b86cead
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test.print.css
@@ -0,0 +1,2 @@
+
+/* This file is for testing CSS file inclusion, no contents are necessary. */
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_cron_helper.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_cron_helper.info
new file mode 100644
index 0000000..2fc8325
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_cron_helper.info
@@ -0,0 +1,12 @@
+name = "Common Test Cron Helper"
+description = "Helper module for CronRunTestCase::testCronExceptions()."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_cron_helper.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_cron_helper.module
new file mode 100644
index 0000000..94a2b2c
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_cron_helper.module
@@ -0,0 +1,17 @@
+<?php
+/**
+ * @file
+ * Helper module for the testCronExceptions in addition to common_test module.
+ */
+
+/**
+ * Implements hook_cron().
+ *
+ * common_test_cron() throws an exception, but the execution should reach this
+ * function as well.
+ *
+ * @see common_test_cron()
+ */
+function common_test_cron_helper_cron() {
+ variable_set('common_test_cron', 'success');
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_info.txt b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_info.txt
new file mode 100644
index 0000000..ae217b9
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/common_test_info.txt
@@ -0,0 +1,9 @@
+; Test parsing with a simple string.
+simple_string = A simple string
+
+; Test that constants can be used as values.
+simple_constant = WATCHDOG_INFO
+
+; After parsing the .info file, 'double_colon' should hold the literal value.
+; Parsing should not throw a fatal error or try to access a class constant.
+double_colon = dummyClassName::
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.info
new file mode 100644
index 0000000..5d0c9c4
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.info
@@ -0,0 +1,12 @@
+name = "Database Test"
+description = "Support module for Database layer tests."
+core = 7.x
+package = Testing
+version = VERSION
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.install b/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.install
new file mode 100644
index 0000000..1136115
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.install
@@ -0,0 +1,221 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the database_test module.
+ */
+
+/**
+ * Implements hook_schema().
+ *
+ * The database tests use the database API which depends on schema
+ * information for certain operations on certain databases.
+ * Therefore, the schema must actually be declared in a normal module
+ * like any other, not directly in the test file.
+ */
+function database_test_schema() {
+ $schema['test'] = array(
+ 'description' => 'Basic test table for the database unit tests.',
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'name' => array(
+ 'description' => "A person's name",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ 'binary' => TRUE,
+ ),
+ 'age' => array(
+ 'description' => "The person's age",
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'job' => array(
+ 'description' => "The person's job",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => 'Undefined',
+ ),
+ ),
+ 'primary key' => array('id'),
+ 'unique keys' => array(
+ 'name' => array('name')
+ ),
+ 'indexes' => array(
+ 'ages' => array('age'),
+ ),
+ );
+
+ // This is an alternate version of the same table that is structured the same
+ // but has a non-serial Primary Key.
+ $schema['test_people'] = array(
+ 'description' => 'A duplicate version of the test table, used for additional tests.',
+ 'fields' => array(
+ 'name' => array(
+ 'description' => "A person's name",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'age' => array(
+ 'description' => "The person's age",
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'job' => array(
+ 'description' => "The person's job",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ ),
+ 'primary key' => array('job'),
+ 'indexes' => array(
+ 'ages' => array('age'),
+ ),
+ );
+
+ $schema['test_people_copy'] = $schema['test_people'];
+ $schema['test_people_copy']['description'] = 'A duplicate version of the test_people table, used for additional tests.';
+
+ $schema['test_one_blob'] = array(
+ 'description' => 'A simple table including a BLOB field for testing BLOB behavior.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'Simple unique ID.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ ),
+ 'blob1' => array(
+ 'description' => 'A BLOB field.',
+ 'type' => 'blob',
+ ),
+ ),
+ 'primary key' => array('id'),
+ );
+
+ $schema['test_two_blobs'] = array(
+ 'description' => 'A simple test table with two BLOB fields.',
+ 'fields' => array(
+ 'id' => array(
+ 'description' => 'Simple unique ID.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ ),
+ 'blob1' => array(
+ 'description' => 'A dummy BLOB field.',
+ 'type' => 'blob',
+ ),
+ 'blob2' => array(
+ 'description' => 'A second BLOB field.',
+ 'type' => 'blob'
+ ),
+ ),
+ 'primary key' => array('id'),
+ );
+
+ $schema['test_task'] = array(
+ 'description' => 'A task list for people in the test table.',
+ 'fields' => array(
+ 'tid' => array(
+ 'description' => 'Task ID, primary key.',
+ 'type' => 'serial',
+ 'not null' => TRUE,
+ ),
+ 'pid' => array(
+ 'description' => 'The {test_people}.pid, foreign key for the test table.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'task' => array(
+ 'description' => 'The task to be completed.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'priority' => array(
+ 'description' => 'The priority of the task.',
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('tid'),
+ );
+
+ $schema['test_null'] = array(
+ 'description' => 'Basic test table for NULL value handling.',
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'name' => array(
+ 'description' => "A person's name.",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ 'default' => '',
+ ),
+ 'age' => array(
+ 'description' => "The person's age.",
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => FALSE,
+ 'default' => 0),
+ ),
+ 'primary key' => array('id'),
+ 'unique keys' => array(
+ 'name' => array('name')
+ ),
+ 'indexes' => array(
+ 'ages' => array('age'),
+ ),
+ );
+
+ $schema['test_serialized'] = array(
+ 'description' => 'Basic test table for NULL value handling.',
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ 'name' => array(
+ 'description' => "A person's name.",
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => FALSE,
+ 'default' => '',
+ ),
+ 'info' => array(
+ 'description' => "The person's data in serialized form.",
+ 'type' => 'blob',
+ 'serialize' => TRUE,
+ ),
+ ),
+ 'primary key' => array('id'),
+ 'unique keys' => array(
+ 'name' => array('name')
+ ),
+ );
+
+ return $schema;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.module
new file mode 100644
index 0000000..6fac319
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.module
@@ -0,0 +1,241 @@
+<?php
+
+/**
+ * Implements hook_query_alter().
+ */
+function database_test_query_alter(QueryAlterableInterface $query) {
+
+ if ($query->hasTag('database_test_alter_add_range')) {
+ $query->range(0, 2);
+ }
+
+ if ($query->hasTag('database_test_alter_add_join')) {
+ $people_alias = $query->join('test', 'people', "test_task.pid = %alias.id");
+ $name_field = $query->addField($people_alias, 'name', 'name');
+ $query->condition($people_alias . '.id', 2);
+ }
+
+ if ($query->hasTag('database_test_alter_change_conditional')) {
+ $conditions =& $query->conditions();
+ $conditions[0]['value'] = 2;
+ }
+
+ if ($query->hasTag('database_test_alter_change_fields')) {
+ $fields =& $query->getFields();
+ unset($fields['age']);
+ }
+
+ if ($query->hasTag('database_test_alter_change_expressions')) {
+ $expressions =& $query->getExpressions();
+ $expressions['double_age']['expression'] = 'age*3';
+ }
+}
+
+
+/**
+ * Implements hook_query_TAG_alter().
+ *
+ * Called by DatabaseTestCase::testAlterRemoveRange.
+ */
+function database_test_query_database_test_alter_remove_range_alter(QueryAlterableInterface $query) {
+ $query->range();
+}
+
+/**
+ * Implements hook_menu().
+ */
+function database_test_menu() {
+ $items['database_test/db_query_temporary'] = array(
+ 'access callback' => TRUE,
+ 'page callback' => 'database_test_db_query_temporary',
+ );
+ $items['database_test/pager_query_even'] = array(
+ 'access callback' => TRUE,
+ 'page callback' => 'database_test_even_pager_query',
+ );
+ $items['database_test/pager_query_odd'] = array(
+ 'access callback' => TRUE,
+ 'page callback' => 'database_test_odd_pager_query',
+ );
+ $items['database_test/tablesort'] = array(
+ 'access callback' => TRUE,
+ 'page callback' => 'database_test_tablesort',
+ );
+ $items['database_test/tablesort_first'] = array(
+ 'access callback' => TRUE,
+ 'page callback' => 'database_test_tablesort_first',
+ );
+ $items['database_test/tablesort_default_sort'] = array(
+ 'access callback' => TRUE,
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('database_test_theme_tablesort'),
+ );
+ return $items;
+}
+
+/**
+ * Run a db_query_temporary and output the table name and its number of rows.
+ *
+ * We need to test that the table created is temporary, so we run it here, in a
+ * separate menu callback request; After this request is done, the temporary
+ * table should automatically dropped.
+ */
+function database_test_db_query_temporary() {
+ $table_name = db_query_temporary('SELECT status FROM {system}', array());
+ drupal_json_output(array(
+ 'table_name' => $table_name,
+ 'row_count' => db_select($table_name)->countQuery()->execute()->fetchField(),
+ ));
+ exit;
+}
+
+/**
+ * Run a pager query and return the results.
+ *
+ * This function does care about the page GET parameter, as set by the
+ * simpletest HTTP call.
+ */
+function database_test_even_pager_query($limit) {
+
+ $query = db_select('test', 't');
+ $query
+ ->fields('t', array('name'))
+ ->orderBy('age');
+
+ // This should result in 2 pages of results.
+ $query = $query->extend('PagerDefault')->limit($limit);
+
+ $names = $query->execute()->fetchCol();
+
+ drupal_json_output(array(
+ 'names' => $names,
+ ));
+ exit;
+}
+
+/**
+ * Run a pager query and return the results.
+ *
+ * This function does care about the page GET parameter, as set by the
+ * simpletest HTTP call.
+ */
+function database_test_odd_pager_query($limit) {
+
+ $query = db_select('test_task', 't');
+ $query
+ ->fields('t', array('task'))
+ ->orderBy('pid');
+
+ // This should result in 4 pages of results.
+ $query = $query->extend('PagerDefault')->limit($limit);
+
+ $names = $query->execute()->fetchCol();
+
+ drupal_json_output(array(
+ 'names' => $names,
+ ));
+ exit;
+}
+
+/**
+ * Run a tablesort query and return the results.
+ *
+ * This function does care about the page GET parameter, as set by the
+ * simpletest HTTP call.
+ */
+function database_test_tablesort() {
+ $header = array(
+ 'tid' => array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc'),
+ 'pid' => array('data' => t('Person ID'), 'field' => 'pid'),
+ 'task' => array('data' => t('Task'), 'field' => 'task'),
+ 'priority' => array('data' => t('Priority'), 'field' => 'priority', ),
+ );
+
+ $query = db_select('test_task', 't');
+ $query
+ ->fields('t', array('tid', 'pid', 'task', 'priority'));
+
+ $query = $query->extend('TableSort')->orderByHeader($header);
+
+ // We need all the results at once to check the sort.
+ $tasks = $query->execute()->fetchAll();
+
+ drupal_json_output(array(
+ 'tasks' => $tasks,
+ ));
+ exit;
+}
+
+/**
+ * Run a tablesort query with a second order_by after and return the results.
+ *
+ * This function does care about the page GET parameter, as set by the
+ * simpletest HTTP call.
+ */
+function database_test_tablesort_first() {
+ $header = array(
+ 'tid' => array('data' => t('Task ID'), 'field' => 'tid', 'sort' => 'desc'),
+ 'pid' => array('data' => t('Person ID'), 'field' => 'pid'),
+ 'task' => array('data' => t('Task'), 'field' => 'task'),
+ 'priority' => array('data' => t('Priority'), 'field' => 'priority', ),
+ );
+
+ $query = db_select('test_task', 't');
+ $query
+ ->fields('t', array('tid', 'pid', 'task', 'priority'));
+
+ $query = $query->extend('TableSort')->orderByHeader($header)->orderBy('priority');
+
+ // We need all the results at once to check the sort.
+ $tasks = $query->execute()->fetchAll();
+
+ drupal_json_output(array(
+ 'tasks' => $tasks,
+ ));
+ exit;
+}
+
+/**
+ * Output a form without setting a header sort.
+ */
+function database_test_theme_tablesort($form, &$form_state) {
+ $header = array(
+ 'username' => array('data' => t('Username'), 'field' => 'u.name'),
+ 'status' => array('data' => t('Status'), 'field' => 'u.status'),
+ );
+
+ $query = db_select('users', 'u');
+ $query->condition('u.uid', 0, '<>');
+ user_build_filter_query($query);
+
+ $count_query = clone $query;
+ $count_query->addExpression('COUNT(u.uid)');
+
+ $query = $query->extend('PagerDefault')->extend('TableSort');
+ $query
+ ->fields('u', array('uid', 'name', 'status', 'created', 'access'))
+ ->limit(50)
+ ->orderByHeader($header)
+ ->setCountQuery($count_query);
+ $result = $query->execute();
+
+ $options = array();
+
+ $status = array(t('blocked'), t('active'));
+ $accounts = array();
+ foreach ($result as $account) {
+ $options[$account->uid] = array(
+ 'username' => check_plain($account->name),
+ 'status' => $status[$account->status],
+ );
+ }
+
+ $form['accounts'] = array(
+ '#type' => 'tableselect',
+ '#header' => $header,
+ '#options' => $options,
+ '#empty' => t('No people available.'),
+ );
+
+ return $form;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.test
new file mode 100644
index 0000000..aa390bd
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/database_test.test
@@ -0,0 +1,4111 @@
+<?php
+
+/**
+ * Dummy class for fetching into a class.
+ *
+ * PDO supports using a new instance of an arbitrary class for records
+ * rather than just a stdClass or array. This class is for testing that
+ * functionality. (See testQueryFetchClass() below)
+ */
+class FakeRecord { }
+
+/**
+ * Base test class for databases.
+ *
+ * Because all database tests share the same test data, we can centralize that
+ * here.
+ */
+class DatabaseTestCase extends DrupalWebTestCase {
+ protected $profile = 'testing';
+
+ function setUp() {
+ parent::setUp('database_test');
+
+ $schema['test'] = drupal_get_schema('test');
+ $schema['test_people'] = drupal_get_schema('test_people');
+ $schema['test_people_copy'] = drupal_get_schema('test_people_copy');
+ $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
+ $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
+ $schema['test_task'] = drupal_get_schema('test_task');
+
+ $this->installTables($schema);
+
+ $this->addSampleData();
+ }
+
+ /**
+ * Set up several tables needed by a certain test.
+ *
+ * @param $schema
+ * An array of table definitions to install.
+ */
+ function installTables($schema) {
+ // This ends up being a test for table drop and create, too, which is nice.
+ foreach ($schema as $name => $data) {
+ if (db_table_exists($name)) {
+ db_drop_table($name);
+ }
+ db_create_table($name, $data);
+ }
+
+ foreach ($schema as $name => $data) {
+ $this->assertTrue(db_table_exists($name), format_string('Table @name created successfully.', array('@name' => $name)));
+ }
+ }
+
+ /**
+ * Set up tables for NULL handling.
+ */
+ function ensureSampleDataNull() {
+ $schema['test_null'] = drupal_get_schema('test_null');
+ $this->installTables($schema);
+
+ db_insert('test_null')
+ ->fields(array('name', 'age'))
+ ->values(array(
+ 'name' => 'Kermit',
+ 'age' => 25,
+ ))
+ ->values(array(
+ 'name' => 'Fozzie',
+ 'age' => NULL,
+ ))
+ ->values(array(
+ 'name' => 'Gonzo',
+ 'age' => 27,
+ ))
+ ->execute();
+ }
+
+ /**
+ * Setup our sample data.
+ *
+ * These are added using db_query(), since we're not trying to test the
+ * INSERT operations here, just populate.
+ */
+ function addSampleData() {
+ // We need the IDs, so we can't use a multi-insert here.
+ $john = db_insert('test')
+ ->fields(array(
+ 'name' => 'John',
+ 'age' => 25,
+ 'job' => 'Singer',
+ ))
+ ->execute();
+
+ $george = db_insert('test')
+ ->fields(array(
+ 'name' => 'George',
+ 'age' => 27,
+ 'job' => 'Singer',
+ ))
+ ->execute();
+
+ $ringo = db_insert('test')
+ ->fields(array(
+ 'name' => 'Ringo',
+ 'age' => 28,
+ 'job' => 'Drummer',
+ ))
+ ->execute();
+
+ $paul = db_insert('test')
+ ->fields(array(
+ 'name' => 'Paul',
+ 'age' => 26,
+ 'job' => 'Songwriter',
+ ))
+ ->execute();
+
+ db_insert('test_people')
+ ->fields(array(
+ 'name' => 'Meredith',
+ 'age' => 30,
+ 'job' => 'Speaker',
+ ))
+ ->execute();
+
+ db_insert('test_task')
+ ->fields(array('pid', 'task', 'priority'))
+ ->values(array(
+ 'pid' => $john,
+ 'task' => 'eat',
+ 'priority' => 3,
+ ))
+ ->values(array(
+ 'pid' => $john,
+ 'task' => 'sleep',
+ 'priority' => 4,
+ ))
+ ->values(array(
+ 'pid' => $john,
+ 'task' => 'code',
+ 'priority' => 1,
+ ))
+ ->values(array(
+ 'pid' => $george,
+ 'task' => 'sing',
+ 'priority' => 2,
+ ))
+ ->values(array(
+ 'pid' => $george,
+ 'task' => 'sleep',
+ 'priority' => 2,
+ ))
+ ->values(array(
+ 'pid' => $paul,
+ 'task' => 'found new band',
+ 'priority' => 1,
+ ))
+ ->values(array(
+ 'pid' => $paul,
+ 'task' => 'perform at superbowl',
+ 'priority' => 3,
+ ))
+ ->execute();
+ }
+}
+
+/**
+ * Test connection management.
+ */
+class DatabaseConnectionTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Connection tests',
+ 'description' => 'Tests of the core database system.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that connections return appropriate connection objects.
+ */
+ function testConnectionRouting() {
+ // Clone the master credentials to a slave connection.
+ // Note this will result in two independent connection objects that happen
+ // to point to the same place.
+ $connection_info = Database::getConnectionInfo('default');
+ Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+
+ $db1 = Database::getConnection('default', 'default');
+ $db2 = Database::getConnection('slave', 'default');
+
+ $this->assertNotNull($db1, 'default connection is a real connection object.');
+ $this->assertNotNull($db2, 'slave connection is a real connection object.');
+ $this->assertNotIdentical($db1, $db2, 'Each target refers to a different connection.');
+
+ // Try to open those targets another time, that should return the same objects.
+ $db1b = Database::getConnection('default', 'default');
+ $db2b = Database::getConnection('slave', 'default');
+ $this->assertIdentical($db1, $db1b, 'A second call to getConnection() returns the same object.');
+ $this->assertIdentical($db2, $db2b, 'A second call to getConnection() returns the same object.');
+
+ // Try to open an unknown target.
+ $unknown_target = $this->randomName();
+ $db3 = Database::getConnection($unknown_target, 'default');
+ $this->assertNotNull($db3, 'Opening an unknown target returns a real connection object.');
+ $this->assertIdentical($db1, $db3, 'An unknown target opens the default connection.');
+
+ // Try to open that unknown target another time, that should return the same object.
+ $db3b = Database::getConnection($unknown_target, 'default');
+ $this->assertIdentical($db3, $db3b, 'A second call to getConnection() returns the same object.');
+ }
+
+ /**
+ * Test that connections return appropriate connection objects.
+ */
+ function testConnectionRoutingOverride() {
+ // Clone the master credentials to a slave connection.
+ // Note this will result in two independent connection objects that happen
+ // to point to the same place.
+ $connection_info = Database::getConnectionInfo('default');
+ Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+
+ Database::ignoreTarget('default', 'slave');
+
+ $db1 = Database::getConnection('default', 'default');
+ $db2 = Database::getConnection('slave', 'default');
+
+ $this->assertIdentical($db1, $db2, 'Both targets refer to the same connection.');
+ }
+
+ /**
+ * Tests the closing of a database connection.
+ */
+ function testConnectionClosing() {
+ // Open the default target so we have an object to compare.
+ $db1 = Database::getConnection('default', 'default');
+
+ // Try to close the the default connection, then open a new one.
+ Database::closeConnection('default', 'default');
+ $db2 = Database::getConnection('default', 'default');
+
+ // Opening a connection after closing it should yield an object different than the original.
+ $this->assertNotIdentical($db1, $db2, 'Opening the default connection after it is closed returns a new object.');
+ }
+
+ /**
+ * Tests the connection options of the active database.
+ */
+ function testConnectionOptions() {
+ $connection_info = Database::getConnectionInfo('default');
+
+ // Be sure we're connected to the default database.
+ $db = Database::getConnection('default', 'default');
+ $connectionOptions = $db->getConnectionOptions();
+
+ // In the MySQL driver, the port can be different, so check individual
+ // options.
+ $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], 'The default connection info driver matches the current connection options driver.');
+ $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], 'The default connection info database matches the current connection options database.');
+
+ // Set up identical slave and confirm connection options are identical.
+ Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+ $db2 = Database::getConnection('slave', 'default');
+ $connectionOptions2 = $db2->getConnectionOptions();
+
+ // Get a fresh copy of the default connection options.
+ $connectionOptions = $db->getConnectionOptions();
+ $this->assertIdentical($connectionOptions, $connectionOptions2, 'The default and slave connection options are identical.');
+
+ // Set up a new connection with different connection info.
+ $test = $connection_info['default'];
+ $test['database'] .= 'test';
+ Database::addConnectionInfo('test', 'default', $test);
+ $connection_info = Database::getConnectionInfo('test');
+
+ // Get a fresh copy of the default connection options.
+ $connectionOptions = $db->getConnectionOptions();
+ $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.');
+ }
+}
+
+/**
+ * Test cloning Select queries.
+ */
+class DatabaseSelectCloneTest extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Select tests, cloning',
+ 'description' => 'Test cloning Select queries.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that subqueries as value within conditions are cloned properly.
+ */
+ function testSelectConditionSubQueryCloning() {
+ $subquery = db_select('test', 't');
+ $subquery->addField('t', 'id', 'id');
+ $subquery->condition('age', 28, '<');
+
+ $query = db_select('test', 't');
+ $query->addField('t', 'name', 'name');
+ $query->condition('id', $subquery, 'IN');
+
+ $clone = clone $query;
+ // Cloned query should not be altered by the following modification
+ // happening on original query.
+ $subquery->condition('age', 25, '>');
+
+ $clone_result = $clone->countQuery()->execute()->fetchField();
+ $query_result = $query->countQuery()->execute()->fetchField();
+
+ // Make sure the cloned query has not been modified
+ $this->assertEqual(3, $clone_result, 'The cloned query returns the expected number of rows');
+ $this->assertEqual(2, $query_result, 'The query returns the expected number of rows');
+ }
+}
+
+/**
+ * Test fetch actions, part 1.
+ *
+ * We get timeout errors if we try to run too many tests at once.
+ */
+class DatabaseFetchTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Fetch tests',
+ 'description' => 'Test the Database system\'s various fetch capabilities.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that we can fetch a record properly in default object mode.
+ */
+ function testQueryFetchDefault() {
+ $records = array();
+ $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25));
+ $this->assertTrue($result instanceof DatabaseStatementInterface, 'Result set is a Drupal statement object.');
+ foreach ($result as $record) {
+ $records[] = $record;
+ $this->assertTrue(is_object($record), 'Record is an object.');
+ $this->assertIdentical($record->name, 'John', '25 year old is John.');
+ }
+
+ $this->assertIdentical(count($records), 1, 'There is only one record.');
+ }
+
+ /**
+ * Confirm that we can fetch a record to an object explicitly.
+ */
+ function testQueryFetchObject() {
+ $records = array();
+ $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_OBJ));
+ foreach ($result as $record) {
+ $records[] = $record;
+ $this->assertTrue(is_object($record), 'Record is an object.');
+ $this->assertIdentical($record->name, 'John', '25 year old is John.');
+ }
+
+ $this->assertIdentical(count($records), 1, 'There is only one record.');
+ }
+
+ /**
+ * Confirm that we can fetch a record to an array associative explicitly.
+ */
+ function testQueryFetchArray() {
+ $records = array();
+ $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_ASSOC));
+ foreach ($result as $record) {
+ $records[] = $record;
+ if ($this->assertTrue(is_array($record), 'Record is an array.')) {
+ $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.');
+ }
+ }
+
+ $this->assertIdentical(count($records), 1, 'There is only one record.');
+ }
+
+ /**
+ * Confirm that we can fetch a record into a new instance of a custom class.
+ *
+ * @see FakeRecord
+ */
+ function testQueryFetchClass() {
+ $records = array();
+ $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'FakeRecord'));
+ foreach ($result as $record) {
+ $records[] = $record;
+ if ($this->assertTrue($record instanceof FakeRecord, 'Record is an object of class FakeRecord.')) {
+ $this->assertIdentical($record->name, 'John', '25 year old is John.');
+ }
+ }
+
+ $this->assertIdentical(count($records), 1, 'There is only one record.');
+ }
+}
+
+/**
+ * Test fetch actions, part 2.
+ *
+ * We get timeout errors if we try to run too many tests at once.
+ */
+class DatabaseFetch2TestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Fetch tests, part 2',
+ 'description' => 'Test the Database system\'s various fetch capabilities.',
+ 'group' => 'Database',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+ }
+
+ // Confirm that we can fetch a record into an indexed array explicitly.
+ function testQueryFetchNum() {
+ $records = array();
+ $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_NUM));
+ foreach ($result as $record) {
+ $records[] = $record;
+ if ($this->assertTrue(is_array($record), 'Record is an array.')) {
+ $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.');
+ }
+ }
+
+ $this->assertIdentical(count($records), 1, 'There is only one record');
+ }
+
+ /**
+ * Confirm that we can fetch a record into a doubly-keyed array explicitly.
+ */
+ function testQueryFetchBoth() {
+ $records = array();
+ $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_BOTH));
+ foreach ($result as $record) {
+ $records[] = $record;
+ if ($this->assertTrue(is_array($record), 'Record is an array.')) {
+ $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.');
+ $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.');
+ }
+ }
+
+ $this->assertIdentical(count($records), 1, 'There is only one record.');
+ }
+
+ /**
+ * Confirm that we can fetch an entire column of a result set at once.
+ */
+ function testQueryFetchCol() {
+ $records = array();
+ $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
+ $column = $result->fetchCol();
+ $this->assertIdentical(count($column), 3, 'fetchCol() returns the right number of records.');
+
+ $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
+ $i = 0;
+ foreach ($result as $record) {
+ $this->assertIdentical($record->name, $column[$i++], 'Column matches direct accesss.');
+ }
+ }
+}
+
+/**
+ * Test the insert builder.
+ */
+class DatabaseInsertTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Insert tests',
+ 'description' => 'Test the Insert query builder.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test the very basic insert functionality.
+ */
+ function testSimpleInsert() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+ $query = db_insert('test');
+ $query->fields(array(
+ 'name' => 'Yoko',
+ 'age' => '29',
+ ));
+ $query->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+ $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField();
+ $this->assertIdentical($saved_age, '29', 'Can retrieve after inserting.');
+ }
+
+ /**
+ * Test that we can insert multiple records in one query object.
+ */
+ function testMultiInsert() {
+ $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+ $query = db_insert('test');
+ $query->fields(array(
+ 'name' => 'Larry',
+ 'age' => '30',
+ ));
+
+ // We should be able to specify values in any order if named.
+ $query->values(array(
+ 'age' => '31',
+ 'name' => 'Curly',
+ ));
+
+ // We should be able to say "use the field order".
+ // This is not the recommended mechanism for most cases, but it should work.
+ $query->values(array('Moe', '32'));
+ $query->execute();
+
+ $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+ $this->assertIdentical($num_records_before + 3, $num_records_after, 'Record inserts correctly.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
+ $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
+ $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
+ $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
+ }
+
+ /**
+ * Test that an insert object can be reused with new data after it executes.
+ */
+ function testRepeatedInsert() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+ $query = db_insert('test');
+
+ $query->fields(array(
+ 'name' => 'Larry',
+ 'age' => '30',
+ ));
+ $query->execute(); // This should run the insert, but leave the fields intact.
+
+ // We should be able to specify values in any order if named.
+ $query->values(array(
+ 'age' => '31',
+ 'name' => 'Curly',
+ ));
+ $query->execute();
+
+ // We should be able to say "use the field order".
+ $query->values(array('Moe', '32'));
+ $query->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+ $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, 'Record inserts correctly.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
+ $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
+ $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
+ $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
+ }
+
+ /**
+ * Test that we can specify fields without values and specify values later.
+ */
+ function testInsertFieldOnlyDefinintion() {
+ // This is useful for importers, when we want to create a query and define
+ // its fields once, then loop over a multi-insert execution.
+ db_insert('test')
+ ->fields(array('name', 'age'))
+ ->values(array('Larry', '30'))
+ ->values(array('Curly', '31'))
+ ->values(array('Moe', '32'))
+ ->execute();
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
+ $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
+ $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
+ $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
+ }
+
+ /**
+ * Test that inserts return the proper auto-increment ID.
+ */
+ function testInsertLastInsertID() {
+ $id = db_insert('test')
+ ->fields(array(
+ 'name' => 'Larry',
+ 'age' => '30',
+ ))
+ ->execute();
+
+ $this->assertIdentical($id, '5', 'Auto-increment ID returned successfully.');
+ }
+
+ /**
+ * Test that the INSERT INTO ... SELECT (fields) ... syntax works.
+ */
+ function testInsertSelectFields() {
+ $query = db_select('test_people', 'tp');
+ // The query builder will always append expressions after fields.
+ // Add the expression first to test that the insert fields are correctly
+ // re-ordered.
+ $query->addExpression('tp.age', 'age');
+ $query
+ ->fields('tp', array('name','job'))
+ ->condition('tp.name', 'Meredith');
+
+ // The resulting query should be equivalent to:
+ // INSERT INTO test (age, name, job)
+ // SELECT tp.age AS age, tp.name AS name, tp.job AS job
+ // FROM test_people tp
+ // WHERE tp.name = 'Meredith'
+ db_insert('test')
+ ->from($query)
+ ->execute();
+
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
+ $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
+ }
+
+ /**
+ * Tests that the INSERT INTO ... SELECT * ... syntax works.
+ */
+ function testInsertSelectAll() {
+ $query = db_select('test_people', 'tp')
+ ->fields('tp')
+ ->condition('tp.name', 'Meredith');
+
+ // The resulting query should be equivalent to:
+ // INSERT INTO test_people_copy
+ // SELECT *
+ // FROM test_people tp
+ // WHERE tp.name = 'Meredith'
+ db_insert('test_people_copy')
+ ->from($query)
+ ->execute();
+
+ $saved_age = db_query('SELECT age FROM {test_people_copy} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
+ $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
+ }
+}
+
+/**
+ * Insert tests using LOB fields, which are weird on some databases.
+ */
+class DatabaseInsertLOBTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Insert tests, LOB fields',
+ 'description' => 'Test the Insert query builder with LOB fields.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that we can insert a single blob field successfully.
+ */
+ function testInsertOneBlob() {
+ $data = "This is\000a test.";
+ $this->assertTrue(strlen($data) === 15, 'Test data contains a NULL.');
+ $id = db_insert('test_one_blob')
+ ->fields(array('blob1' => $data))
+ ->execute();
+ $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
+ $this->assertTrue($r['blob1'] === $data, format_string('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
+ }
+
+ /**
+ * Test that we can insert multiple blob fields in the same query.
+ */
+ function testInsertMultipleBlob() {
+ $id = db_insert('test_two_blobs')
+ ->fields(array(
+ 'blob1' => 'This is',
+ 'blob2' => 'a test',
+ ))
+ ->execute();
+ $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
+ $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', 'Can insert multiple blobs per row.');
+ }
+}
+
+/**
+ * Insert tests for "database default" values.
+ */
+class DatabaseInsertDefaultsTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Insert tests, default fields',
+ 'description' => 'Test the Insert query builder with default values.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that we can run a query that is "default values for everything".
+ */
+ function testDefaultInsert() {
+ $query = db_insert('test')->useDefaults(array('job'));
+ $id = $query->execute();
+
+ $schema = drupal_get_schema('test');
+
+ $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
+ $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.');
+ }
+
+ /**
+ * Test that no action will be preformed if no fields are specified.
+ */
+ function testDefaultEmptyInsert() {
+ $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+ try {
+ $result = db_insert('test')->execute();
+ // This is only executed if no exception has been thrown.
+ $this->fail('Expected exception NoFieldsException has not been thrown.');
+ } catch (NoFieldsException $e) {
+ $this->pass('Expected exception NoFieldsException has been thrown.');
+ }
+
+ $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+ $this->assertIdentical($num_records_before, $num_records_after, 'Do nothing as no fields are specified.');
+ }
+
+ /**
+ * Test that we can insert fields with values and defaults in the same query.
+ */
+ function testDefaultInsertWithFields() {
+ $query = db_insert('test')
+ ->fields(array('name' => 'Bob'))
+ ->useDefaults(array('job'));
+ $id = $query->execute();
+
+ $schema = drupal_get_schema('test');
+
+ $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
+ $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.');
+ }
+}
+
+/**
+ * Update builder tests.
+ */
+class DatabaseUpdateTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Update tests',
+ 'description' => 'Test the Update query builder.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that we can update a single record successfully.
+ */
+ function testSimpleUpdate() {
+ $num_updated = db_update('test')
+ ->fields(array('name' => 'Tiffany'))
+ ->condition('id', 1)
+ ->execute();
+ $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
+
+ $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField();
+ $this->assertIdentical($saved_name, 'Tiffany', 'Updated name successfully.');
+ }
+
+ /**
+ * Confirm updating to NULL.
+ */
+ function testSimpleNullUpdate() {
+ $this->ensureSampleDataNull();
+ $num_updated = db_update('test_null')
+ ->fields(array('age' => NULL))
+ ->condition('name', 'Kermit')
+ ->execute();
+ $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
+
+ $saved_age = db_query('SELECT age FROM {test_null} WHERE name = :name', array(':name' => 'Kermit'))->fetchField();
+ $this->assertNull($saved_age, 'Updated name successfully.');
+ }
+
+ /**
+ * Confirm that we can update a multiple records successfully.
+ */
+ function testMultiUpdate() {
+ $num_updated = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->condition('job', 'Singer')
+ ->execute();
+ $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
+ }
+
+ /**
+ * Confirm that we can update a multiple records with a non-equality condition.
+ */
+ function testMultiGTUpdate() {
+ $num_updated = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->condition('age', 26, '>')
+ ->execute();
+ $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
+ }
+
+ /**
+ * Confirm that we can update a multiple records with a where call.
+ */
+ function testWhereUpdate() {
+ $num_updated = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->where('age > :age', array(':age' => 26))
+ ->execute();
+ $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
+ }
+
+ /**
+ * Confirm that we can stack condition and where calls.
+ */
+ function testWhereAndConditionUpdate() {
+ $update = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->where('age > :age', array(':age' => 26))
+ ->condition('name', 'Ringo');
+ $num_updated = $update->execute();
+ $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
+ }
+
+ /**
+ * Test updating with expressions.
+ */
+ function testExpressionUpdate() {
+ // Set age = 1 for a single row for this test to work.
+ db_update('test')
+ ->condition('id', 1)
+ ->fields(array('age' => 1))
+ ->execute();
+
+ // Ensure that expressions are handled properly. This should set every
+ // record's age to a square of itself, which will change only three of the
+ // four records in the table since 1*1 = 1. That means only three records
+ // are modified, so we should get back 3, not 4, from execute().
+ $num_rows = db_update('test')
+ ->expression('age', 'age * age')
+ ->execute();
+ $this->assertIdentical($num_rows, 3, 'Number of affected rows are returned.');
+ }
+
+ /**
+ * Confirm that we can update the primary key of a record successfully.
+ */
+ function testPrimaryKeyUpdate() {
+ $num_updated = db_update('test')
+ ->fields(array('id' => 42, 'name' => 'John'))
+ ->condition('id', 1)
+ ->execute();
+ $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
+
+ $saved_name= db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 42))->fetchField();
+ $this->assertIdentical($saved_name, 'John', 'Updated primary key successfully.');
+ }
+}
+
+/**
+ * Tests for more complex update statements.
+ */
+class DatabaseUpdateComplexTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Update tests, Complex',
+ 'description' => 'Test the Update query builder, complex queries.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test updates with OR conditionals.
+ */
+ function testOrConditionUpdate() {
+ $update = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->condition(db_or()
+ ->condition('name', 'John')
+ ->condition('name', 'Paul')
+ );
+ $num_updated = $update->execute();
+ $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
+ }
+
+ /**
+ * Test WHERE IN clauses.
+ */
+ function testInConditionUpdate() {
+ $num_updated = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->condition('name', array('John', 'Paul'), 'IN')
+ ->execute();
+ $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
+ }
+
+ /**
+ * Test WHERE NOT IN clauses.
+ */
+ function testNotInConditionUpdate() {
+ // The o is lowercase in the 'NoT IN' operator, to make sure the operators
+ // work in mixed case.
+ $num_updated = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->condition('name', array('John', 'Paul', 'George'), 'NoT IN')
+ ->execute();
+ $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
+ }
+
+ /**
+ * Test BETWEEN conditional clauses.
+ */
+ function testBetweenConditionUpdate() {
+ $num_updated = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->condition('age', array(25, 26), 'BETWEEN')
+ ->execute();
+ $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
+ }
+
+ /**
+ * Test LIKE conditionals.
+ */
+ function testLikeConditionUpdate() {
+ $num_updated = db_update('test')
+ ->fields(array('job' => 'Musician'))
+ ->condition('name', '%ge%', 'LIKE')
+ ->execute();
+ $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
+ }
+
+ /**
+ * Test update with expression values.
+ */
+ function testUpdateExpression() {
+ $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
+ $GLOBALS['larry_test'] = 1;
+ $num_updated = db_update('test')
+ ->condition('name', 'Ringo')
+ ->fields(array('job' => 'Musician'))
+ ->expression('age', 'age + :age', array(':age' => 4))
+ ->execute();
+ $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
+
+ $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
+ $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
+
+ $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch();
+ $this->assertEqual($person->name, 'Ringo', 'Name set correctly.');
+ $this->assertEqual($person->age, $before_age + 4, 'Age set correctly.');
+ $this->assertEqual($person->job, 'Musician', 'Job set correctly.');
+ $GLOBALS['larry_test'] = 0;
+ }
+
+ /**
+ * Test update with only expression values.
+ */
+ function testUpdateOnlyExpression() {
+ $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
+ $num_updated = db_update('test')
+ ->condition('name', 'Ringo')
+ ->expression('age', 'age + :age', array(':age' => 4))
+ ->execute();
+ $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
+
+ $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
+ $this->assertEqual($before_age + 4, $after_age, 'Age updated correctly');
+ }
+}
+
+/**
+ * Test update queries involving LOB values.
+ */
+class DatabaseUpdateLOBTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Update tests, LOB',
+ 'description' => 'Test the Update query builder with LOB fields.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that we can update a blob column.
+ */
+ function testUpdateOneBlob() {
+ $data = "This is\000a test.";
+ $this->assertTrue(strlen($data) === 15, 'Test data contains a NULL.');
+ $id = db_insert('test_one_blob')
+ ->fields(array('blob1' => $data))
+ ->execute();
+
+ $data .= $data;
+ db_update('test_one_blob')
+ ->condition('id', $id)
+ ->fields(array('blob1' => $data))
+ ->execute();
+
+ $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
+ $this->assertTrue($r['blob1'] === $data, format_string('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
+ }
+
+ /**
+ * Confirm that we can update two blob columns in the same table.
+ */
+ function testUpdateMultipleBlob() {
+ $id = db_insert('test_two_blobs')
+ ->fields(array(
+ 'blob1' => 'This is',
+ 'blob2' => 'a test',
+ ))
+ ->execute();
+
+ db_update('test_two_blobs')
+ ->condition('id', $id)
+ ->fields(array('blob1' => 'and so', 'blob2' => 'is this'))
+ ->execute();
+
+ $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
+ $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', 'Can update multiple blobs per row.');
+ }
+}
+
+/**
+ * Delete/Truncate tests.
+ *
+ * The DELETE tests are not as extensive, as all of the interesting code for
+ * DELETE queries is in the conditional which is identical to the UPDATE and
+ * SELECT conditional handling.
+ *
+ * The TRUNCATE tests are not extensive either, because the behavior of
+ * TRUNCATE queries is not consistent across database engines. We only test
+ * that a TRUNCATE query actually deletes all rows from the target table.
+ */
+class DatabaseDeleteTruncateTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Delete/Truncate tests',
+ 'description' => 'Test the Delete and Truncate query builders.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that we can use a subselect in a delete successfully.
+ */
+ function testSubselectDelete() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
+ $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField();
+
+ $subquery = db_select('test', 't')
+ ->fields('t', array('id'))
+ ->condition('t.id', array($pid_to_delete), 'IN');
+ $delete = db_delete('test_task')
+ ->condition('task', 'sleep')
+ ->condition('pid', $subquery, 'IN');
+
+ $num_deleted = $delete->execute();
+ $this->assertEqual($num_deleted, 1, "Deleted 1 record.");
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
+ $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
+ }
+
+ /**
+ * Confirm that we can delete a single record successfully.
+ */
+ function testSimpleDelete() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+ $num_deleted = db_delete('test')
+ ->condition('id', 1)
+ ->execute();
+ $this->assertIdentical($num_deleted, 1, 'Deleted 1 record.');
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+ $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
+ }
+
+ /**
+ * Confirm that we can truncate a whole table successfully.
+ */
+ function testTruncate() {
+ $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
+
+ db_truncate('test')->execute();
+
+ $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
+ $this->assertEqual(0, $num_records_after, 'Truncate really deletes everything.');
+ }
+}
+
+/**
+ * Test the MERGE query builder.
+ */
+class DatabaseMergeTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Merge tests',
+ 'description' => 'Test the Merge query builder.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that we can merge-insert a record successfully.
+ */
+ function testMergeInsert() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+ $result = db_merge('test_people')
+ ->key(array('job' => 'Presenter'))
+ ->fields(array(
+ 'age' => 31,
+ 'name' => 'Tiffany',
+ ))
+ ->execute();
+
+ $this->assertEqual($result, MergeQuery::STATUS_INSERT, 'Insert status returned.');
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+ $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.');
+
+ $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
+ $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
+ $this->assertEqual($person->age, 31, 'Age set correctly.');
+ $this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
+ }
+
+ /**
+ * Confirm that we can merge-update a record successfully.
+ */
+ function testMergeUpdate() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+ $result = db_merge('test_people')
+ ->key(array('job' => 'Speaker'))
+ ->fields(array(
+ 'age' => 31,
+ 'name' => 'Tiffany',
+ ))
+ ->execute();
+
+ $this->assertEqual($result, MergeQuery::STATUS_UPDATE, 'Update status returned.');
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+ $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
+
+ $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+ $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
+ $this->assertEqual($person->age, 31, 'Age set correctly.');
+ $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
+ }
+
+ /**
+ * Confirm that we can merge-update a record successfully, with different insert and update.
+ */
+ function testMergeUpdateExcept() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+ db_merge('test_people')
+ ->key(array('job' => 'Speaker'))
+ ->insertFields(array('age' => 31))
+ ->updateFields(array('name' => 'Tiffany'))
+ ->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+ $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
+
+ $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+ $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
+ $this->assertEqual($person->age, 30, 'Age skipped correctly.');
+ $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
+ }
+
+ /**
+ * Confirm that we can merge-update a record successfully, with alternate replacement.
+ */
+ function testMergeUpdateExplicit() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+ db_merge('test_people')
+ ->key(array('job' => 'Speaker'))
+ ->insertFields(array(
+ 'age' => 31,
+ 'name' => 'Tiffany',
+ ))
+ ->updateFields(array(
+ 'name' => 'Joe',
+ ))
+ ->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+ $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
+
+ $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+ $this->assertEqual($person->name, 'Joe', 'Name set correctly.');
+ $this->assertEqual($person->age, 30, 'Age skipped correctly.');
+ $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
+ }
+
+ /**
+ * Confirm that we can merge-update a record successfully, with expressions.
+ */
+ function testMergeUpdateExpression() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+ $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField();
+
+ // This is a very contrived example, as I have no idea why you'd want to
+ // change age this way, but that's beside the point.
+ // Note that we are also double-setting age here, once as a literal and
+ // once as an expression. This test will only pass if the expression wins,
+ // which is what is supposed to happen.
+ db_merge('test_people')
+ ->key(array('job' => 'Speaker'))
+ ->fields(array('name' => 'Tiffany'))
+ ->insertFields(array('age' => 31))
+ ->expression('age', 'age + :age', array(':age' => 4))
+ ->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+ $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
+
+ $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+ $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
+ $this->assertEqual($person->age, $age_before + 4, 'Age updated correctly.');
+ $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
+ }
+
+ /**
+ * Test that we can merge-insert without any update fields.
+ */
+ function testMergeInsertWithoutUpdate() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+ db_merge('test_people')
+ ->key(array('job' => 'Presenter'))
+ ->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+ $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.');
+
+ $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
+ $this->assertEqual($person->name, '', 'Name set correctly.');
+ $this->assertEqual($person->age, 0, 'Age set correctly.');
+ $this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
+ }
+
+ /**
+ * Confirm that we can merge-update without any update fields.
+ */
+ function testMergeUpdateWithoutUpdate() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+ db_merge('test_people')
+ ->key(array('job' => 'Speaker'))
+ ->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+ $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.');
+
+ $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+ $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.');
+ $this->assertEqual($person->age, 30, 'Age skipped correctly.');
+ $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.');
+
+ db_merge('test_people')
+ ->key(array('job' => 'Speaker'))
+ ->insertFields(array('age' => 31))
+ ->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+ $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.');
+
+ $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
+ $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.');
+ $this->assertEqual($person->age, 30, 'Age skipped correctly.');
+ $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.');
+ }
+
+ /**
+ * Test that an invalid merge query throws an exception like it is supposed to.
+ */
+ function testInvalidMerge() {
+ try {
+ // This query should die because there is no key field specified.
+ db_merge('test_people')
+ ->fields(array(
+ 'age' => 31,
+ 'name' => 'Tiffany',
+ ))
+ ->execute();
+ }
+ catch (InvalidMergeQueryException $e) {
+ $this->pass('InvalidMergeQueryException thrown for invalid query.');
+ return;
+ }
+ $this->fail('No InvalidMergeQueryException thrown');
+ }
+}
+
+/**
+ * Test the SELECT builder.
+ */
+class DatabaseSelectTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Select tests',
+ 'description' => 'Test the Select query builder.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test rudimentary SELECT statements.
+ */
+ function testSimpleSelect() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $result = $query->execute();
+
+ $num_records = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ }
+
+ $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test rudimentary SELECT statement with a COMMENT.
+ */
+ function testSimpleComment() {
+ $query = db_select('test')->comment('Testing query comments');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $result = $query->execute();
+
+ $num_records = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ }
+
+ $query = (string)$query;
+ $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
+
+ $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
+ $this->assertEqual($query, $expected, 'The flattened query contains the comment string.');
+ }
+
+ /**
+ * Test query COMMENT system against vulnerabilities.
+ */
+ function testVulnerableComment() {
+ $query = db_select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $result = $query->execute();
+
+ $num_records = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ }
+
+ $query = (string)$query;
+ $expected = "/* Testing query comments SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
+
+ $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
+ $this->assertEqual($query, $expected, 'The flattened query contains the sanitised comment string.');
+ }
+
+ /**
+ * Test basic conditionals on SELECT statements.
+ */
+ function testSimpleSelectConditional() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $query->condition('age', 27);
+ $result = $query->execute();
+
+ // Check that the aliases are being created the way we want.
+ $this->assertEqual($name_field, 'name', 'Name field alias is correct.');
+ $this->assertEqual($age_field, 'age', 'Age field alias is correct.');
+
+ // Ensure that we got the right record.
+ $record = $result->fetch();
+ $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
+ $this->assertEqual($record->$age_field, 27, 'Fetched age is correct.');
+ }
+
+ /**
+ * Test SELECT statements with expressions.
+ */
+ function testSimpleSelectExpression() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addExpression("age*2", 'double_age');
+ $query->condition('age', 27);
+ $result = $query->execute();
+
+ // Check that the aliases are being created the way we want.
+ $this->assertEqual($name_field, 'name', 'Name field alias is correct.');
+ $this->assertEqual($age_field, 'double_age', 'Age field alias is correct.');
+
+ // Ensure that we got the right record.
+ $record = $result->fetch();
+ $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
+ $this->assertEqual($record->$age_field, 27*2, 'Fetched age expression is correct.');
+ }
+
+ /**
+ * Test SELECT statements with multiple expressions.
+ */
+ function testSimpleSelectExpressionMultiple() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_double_field = $query->addExpression("age*2");
+ $age_triple_field = $query->addExpression("age*3");
+ $query->condition('age', 27);
+ $result = $query->execute();
+
+ // Check that the aliases are being created the way we want.
+ $this->assertEqual($age_double_field, 'expression', 'Double age field alias is correct.');
+ $this->assertEqual($age_triple_field, 'expression_2', 'Triple age field alias is correct.');
+
+ // Ensure that we got the right record.
+ $record = $result->fetch();
+ $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
+ $this->assertEqual($record->$age_double_field, 27*2, 'Fetched double age expression is correct.');
+ $this->assertEqual($record->$age_triple_field, 27*3, 'Fetched triple age expression is correct.');
+ }
+
+ /**
+ * Test adding multiple fields to a select statement at the same time.
+ */
+ function testSimpleSelectMultipleFields() {
+ $record = db_select('test')
+ ->fields('test', array('id', 'name', 'age', 'job'))
+ ->condition('age', 27)
+ ->execute()->fetchObject();
+
+ // Check that all fields we asked for are present.
+ $this->assertNotNull($record->id, 'ID field is present.');
+ $this->assertNotNull($record->name, 'Name field is present.');
+ $this->assertNotNull($record->age, 'Age field is present.');
+ $this->assertNotNull($record->job, 'Job field is present.');
+
+ // Ensure that we got the right record.
+ // Check that all fields we asked for are present.
+ $this->assertEqual($record->id, 2, 'ID field has the correct value.');
+ $this->assertEqual($record->name, 'George', 'Name field has the correct value.');
+ $this->assertEqual($record->age, 27, 'Age field has the correct value.');
+ $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
+ }
+
+ /**
+ * Test adding all fields from a given table to a select statement.
+ */
+ function testSimpleSelectAllFields() {
+ $record = db_select('test')
+ ->fields('test')
+ ->condition('age', 27)
+ ->execute()->fetchObject();
+
+ // Check that all fields we asked for are present.
+ $this->assertNotNull($record->id, 'ID field is present.');
+ $this->assertNotNull($record->name, 'Name field is present.');
+ $this->assertNotNull($record->age, 'Age field is present.');
+ $this->assertNotNull($record->job, 'Job field is present.');
+
+ // Ensure that we got the right record.
+ // Check that all fields we asked for are present.
+ $this->assertEqual($record->id, 2, 'ID field has the correct value.');
+ $this->assertEqual($record->name, 'George', 'Name field has the correct value.');
+ $this->assertEqual($record->age, 27, 'Age field has the correct value.');
+ $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
+ }
+
+ /**
+ * Test that we can find a record with a NULL value.
+ */
+ function testNullCondition() {
+ $this->ensureSampleDataNull();
+
+ $names = db_select('test_null', 'tn')
+ ->fields('tn', array('name'))
+ ->isNull('age')
+ ->execute()->fetchCol();
+
+ $this->assertEqual(count($names), 1, 'Correct number of records found with NULL age.');
+ $this->assertEqual($names[0], 'Fozzie', 'Correct record returned for NULL age.');
+ }
+
+ /**
+ * Test that we can find a record without a NULL value.
+ */
+ function testNotNullCondition() {
+ $this->ensureSampleDataNull();
+
+ $names = db_select('test_null', 'tn')
+ ->fields('tn', array('name'))
+ ->isNotNull('tn.age')
+ ->orderBy('name')
+ ->execute()->fetchCol();
+
+ $this->assertEqual(count($names), 2, 'Correct number of records found withNOT NULL age.');
+ $this->assertEqual($names[0], 'Gonzo', 'Correct record returned for NOT NULL age.');
+ $this->assertEqual($names[1], 'Kermit', 'Correct record returned for NOT NULL age.');
+ }
+
+ /**
+ * Test that we can UNION multiple Select queries together. This is
+ * semantically equal to UNION DISTINCT, so we don't explicity test that.
+ */
+ function testUnion() {
+ $query_1 = db_select('test', 't')
+ ->fields('t', array('name'))
+ ->condition('age', array(27, 28), 'IN');
+
+ $query_2 = db_select('test', 't')
+ ->fields('t', array('name'))
+ ->condition('age', 28);
+
+ $query_1->union($query_2);
+
+ $names = $query_1->execute()->fetchCol();
+
+ // Ensure we only get 2 records.
+ $this->assertEqual(count($names), 2, 'UNION correctly discarded duplicates.');
+
+ $this->assertEqual($names[0], 'George', 'First query returned correct name.');
+ $this->assertEqual($names[1], 'Ringo', 'Second query returned correct name.');
+ }
+
+ /**
+ * Test that we can UNION ALL multiple Select queries together.
+ */
+ function testUnionAll() {
+ $query_1 = db_select('test', 't')
+ ->fields('t', array('name'))
+ ->condition('age', array(27, 28), 'IN');
+
+ $query_2 = db_select('test', 't')
+ ->fields('t', array('name'))
+ ->condition('age', 28);
+
+ $query_1->union($query_2, 'ALL');
+
+ $names = $query_1->execute()->fetchCol();
+
+ // Ensure we get all 3 records.
+ $this->assertEqual(count($names), 3, 'UNION ALL correctly preserved duplicates.');
+
+ $this->assertEqual($names[0], 'George', 'First query returned correct first name.');
+ $this->assertEqual($names[1], 'Ringo', 'Second query returned correct second name.');
+ $this->assertEqual($names[2], 'Ringo', 'Third query returned correct name.');
+ }
+
+ /**
+ * Test that random ordering of queries works.
+ *
+ * We take the approach of testing the Drupal layer only, rather than trying
+ * to test that the database's random number generator actually produces
+ * random queries (which is very difficult to do without an unacceptable risk
+ * of the test failing by accident).
+ *
+ * Therefore, in this test we simply run the same query twice and assert that
+ * the two results are reordered versions of each other (as well as of the
+ * same query without the random ordering). It is reasonable to assume that
+ * if we run the same select query twice and the results are in a different
+ * order each time, the only way this could happen is if we have successfully
+ * triggered the database's random ordering functionality.
+ */
+ function testRandomOrder() {
+ // Use 52 items, so the chance that this test fails by accident will be the
+ // same as the chance that a deck of cards will come out in the same order
+ // after shuffling it (in other words, nearly impossible).
+ $number_of_items = 52;
+ while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) {
+ db_insert('test')->fields(array('name' => $this->randomName()))->execute();
+ }
+
+ // First select the items in order and make sure we get an ordered list.
+ $expected_ids = range(1, $number_of_items);
+ $ordered_ids = db_select('test', 't')
+ ->fields('t', array('id'))
+ ->range(0, $number_of_items)
+ ->orderBy('id')
+ ->execute()
+ ->fetchCol();
+ $this->assertEqual($ordered_ids, $expected_ids, 'A query without random ordering returns IDs in the correct order.');
+
+ // Now perform the same query, but instead choose a random ordering. We
+ // expect this to contain a differently ordered version of the original
+ // result.
+ $randomized_ids = db_select('test', 't')
+ ->fields('t', array('id'))
+ ->range(0, $number_of_items)
+ ->orderRandom()
+ ->execute()
+ ->fetchCol();
+ $this->assertNotEqual($randomized_ids, $ordered_ids, 'A query with random ordering returns an unordered set of IDs.');
+ $sorted_ids = $randomized_ids;
+ sort($sorted_ids);
+ $this->assertEqual($sorted_ids, $ordered_ids, 'After sorting the random list, the result matches the original query.');
+
+ // Now perform the exact same query again, and make sure the order is
+ // different.
+ $randomized_ids_second_set = db_select('test', 't')
+ ->fields('t', array('id'))
+ ->range(0, $number_of_items)
+ ->orderRandom()
+ ->execute()
+ ->fetchCol();
+ $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, 'Performing the query with random ordering a second time returns IDs in a different order.');
+ $sorted_ids_second_set = $randomized_ids_second_set;
+ sort($sorted_ids_second_set);
+ $this->assertEqual($sorted_ids_second_set, $sorted_ids, 'After sorting the second random list, the result matches the sorted version of the first random list.');
+ }
+
+ /**
+ * Test that aliases are renamed when duplicates.
+ */
+ function testSelectDuplicateAlias() {
+ $query = db_select('test', 't');
+ $alias1 = $query->addField('t', 'name', 'the_alias');
+ $alias2 = $query->addField('t', 'age', 'the_alias');
+ $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.');
+ }
+}
+
+/**
+ * Test case for subselects in a dynamic SELECT query.
+ */
+class DatabaseSelectSubqueryTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Select tests, subqueries',
+ 'description' => 'Test the Select query builder.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that we can use a subquery in a FROM clause.
+ */
+ function testFromSubquerySelect() {
+ // Create a subquery, which is just a normal query object.
+ $subquery = db_select('test_task', 'tt');
+ $subquery->addField('tt', 'pid', 'pid');
+ $subquery->addField('tt', 'task', 'task');
+ $subquery->condition('priority', 1);
+
+ for ($i = 0; $i < 2; $i++) {
+ // Create another query that joins against the virtual table resulting
+ // from the subquery.
+ $select = db_select($subquery, 'tt2');
+ $select->join('test', 't', 't.id=tt2.pid');
+ $select->addField('t', 'name');
+ if ($i) {
+ // Use a different number of conditions here to confuse the subquery
+ // placeholder counter, testing http://drupal.org/node/1112854.
+ $select->condition('name', 'John');
+ }
+ $select->condition('task', 'code');
+
+ // The resulting query should be equivalent to:
+ // SELECT t.name
+ // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt
+ // INNER JOIN test t ON t.id=tt.pid
+ // WHERE tt.task = 'code'
+ $people = $select->execute()->fetchCol();
+
+ $this->assertEqual(count($people), 1, 'Returned the correct number of rows.');
+ }
+ }
+
+ /**
+ * Test that we can use a subquery in a FROM clause with a limit.
+ */
+ function testFromSubquerySelectWithLimit() {
+ // Create a subquery, which is just a normal query object.
+ $subquery = db_select('test_task', 'tt');
+ $subquery->addField('tt', 'pid', 'pid');
+ $subquery->addField('tt', 'task', 'task');
+ $subquery->orderBy('priority', 'DESC');
+ $subquery->range(0, 1);
+
+ // Create another query that joins against the virtual table resulting
+ // from the subquery.
+ $select = db_select($subquery, 'tt2');
+ $select->join('test', 't', 't.id=tt2.pid');
+ $select->addField('t', 'name');
+
+ // The resulting query should be equivalent to:
+ // SELECT t.name
+ // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt
+ // INNER JOIN test t ON t.id=tt.pid
+ $people = $select->execute()->fetchCol();
+
+ $this->assertEqual(count($people), 1, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test that we can use a subquery in a WHERE clause.
+ */
+ function testConditionSubquerySelect() {
+ // Create a subquery, which is just a normal query object.
+ $subquery = db_select('test_task', 'tt');
+ $subquery->addField('tt', 'pid', 'pid');
+ $subquery->condition('tt.priority', 1);
+
+ // Create another query that joins against the virtual table resulting
+ // from the subquery.
+ $select = db_select('test_task', 'tt2');
+ $select->addField('tt2', 'task');
+ $select->condition('tt2.pid', $subquery, 'IN');
+
+ // The resulting query should be equivalent to:
+ // SELECT tt2.name
+ // FROM test tt2
+ // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1)
+ $people = $select->execute()->fetchCol();
+ $this->assertEqual(count($people), 5, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test that we can use a subquery in a JOIN clause.
+ */
+ function testJoinSubquerySelect() {
+ // Create a subquery, which is just a normal query object.
+ $subquery = db_select('test_task', 'tt');
+ $subquery->addField('tt', 'pid', 'pid');
+ $subquery->condition('priority', 1);
+
+ // Create another query that joins against the virtual table resulting
+ // from the subquery.
+ $select = db_select('test', 't');
+ $select->join($subquery, 'tt', 't.id=tt.pid');
+ $select->addField('t', 'name');
+
+ // The resulting query should be equivalent to:
+ // SELECT t.name
+ // FROM test t
+ // INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid
+ $people = $select->execute()->fetchCol();
+
+ $this->assertEqual(count($people), 2, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test EXISTS subquery conditionals on SELECT statements.
+ *
+ * We essentially select all rows from the {test} table that have matching
+ * rows in the {test_people} table based on the shared name column.
+ */
+ function testExistsSubquerySelect() {
+ // Put George into {test_people}.
+ db_insert('test_people')
+ ->fields(array(
+ 'name' => 'George',
+ 'age' => 27,
+ 'job' => 'Singer',
+ ))
+ ->execute();
+ // Base query to {test}.
+ $query = db_select('test', 't')
+ ->fields('t', array('name'));
+ // Subquery to {test_people}.
+ $subquery = db_select('test_people', 'tp')
+ ->fields('tp', array('name'))
+ ->where('tp.name = t.name');
+ $query->exists($subquery);
+ $result = $query->execute();
+
+ // Ensure that we got the right record.
+ $record = $result->fetch();
+ $this->assertEqual($record->name, 'George', 'Fetched name is correct using EXISTS query.');
+ }
+
+ /**
+ * Test NOT EXISTS subquery conditionals on SELECT statements.
+ *
+ * We essentially select all rows from the {test} table that don't have
+ * matching rows in the {test_people} table based on the shared name column.
+ */
+ function testNotExistsSubquerySelect() {
+ // Put George into {test_people}.
+ db_insert('test_people')
+ ->fields(array(
+ 'name' => 'George',
+ 'age' => 27,
+ 'job' => 'Singer',
+ ))
+ ->execute();
+
+ // Base query to {test}.
+ $query = db_select('test', 't')
+ ->fields('t', array('name'));
+ // Subquery to {test_people}.
+ $subquery = db_select('test_people', 'tp')
+ ->fields('tp', array('name'))
+ ->where('tp.name = t.name');
+ $query->notExists($subquery);
+
+ // Ensure that we got the right number of records.
+ $people = $query->execute()->fetchCol();
+ $this->assertEqual(count($people), 3, 'NOT EXISTS query returned the correct results.');
+ }
+}
+
+/**
+ * Test select with order by clauses.
+ */
+class DatabaseSelectOrderedTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Select tests, ordered',
+ 'description' => 'Test the Select query builder.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test basic order by.
+ */
+ function testSimpleSelectOrdered() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $query->orderBy($age_field);
+ $result = $query->execute();
+
+ $num_records = 0;
+ $last_age = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ $this->assertTrue($record->age >= $last_age, 'Results returned in correct order.');
+ $last_age = $record->age;
+ }
+
+ $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test multiple order by.
+ */
+ function testSimpleSelectMultiOrdered() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $job_field = $query->addField('test', 'job');
+ $query->orderBy($job_field);
+ $query->orderBy($age_field);
+ $result = $query->execute();
+
+ $num_records = 0;
+ $expected = array(
+ array('Ringo', 28, 'Drummer'),
+ array('John', 25, 'Singer'),
+ array('George', 27, 'Singer'),
+ array('Paul', 26, 'Songwriter'),
+ );
+ $results = $result->fetchAll(PDO::FETCH_NUM);
+ foreach ($expected as $k => $record) {
+ $num_records++;
+ foreach ($record as $kk => $col) {
+ if ($expected[$k][$kk] != $results[$k][$kk]) {
+ $this->assertTrue(FALSE, 'Results returned in correct order.');
+ }
+ }
+ }
+ $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test order by descending.
+ */
+ function testSimpleSelectOrderedDesc() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $query->orderBy($age_field, 'DESC');
+ $result = $query->execute();
+
+ $num_records = 0;
+ $last_age = 100000000;
+ foreach ($result as $record) {
+ $num_records++;
+ $this->assertTrue($record->age <= $last_age, 'Results returned in correct order.');
+ $last_age = $record->age;
+ }
+
+ $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
+ }
+}
+
+/**
+ * Test more complex select statements.
+ */
+class DatabaseSelectComplexTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Select tests, complex',
+ 'description' => 'Test the Select query builder with more complex queries.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test simple JOIN statements.
+ */
+ function testDefaultJoin() {
+ $query = db_select('test_task', 't');
+ $people_alias = $query->join('test', 'p', 't.pid = p.id');
+ $name_field = $query->addField($people_alias, 'name', 'name');
+ $task_field = $query->addField('t', 'task', 'task');
+ $priority_field = $query->addField('t', 'priority', 'priority');
+
+ $query->orderBy($priority_field);
+ $result = $query->execute();
+
+ $num_records = 0;
+ $last_priority = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ $this->assertTrue($record->$priority_field >= $last_priority, 'Results returned in correct order.');
+ $this->assertNotEqual($record->$name_field, 'Ringo', 'Taskless person not selected.');
+ $last_priority = $record->$priority_field;
+ }
+
+ $this->assertEqual($num_records, 7, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test LEFT OUTER joins.
+ */
+ function testLeftOuterJoin() {
+ $query = db_select('test', 'p');
+ $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id');
+ $name_field = $query->addField('p', 'name', 'name');
+ $task_field = $query->addField($people_alias, 'task', 'task');
+ $priority_field = $query->addField($people_alias, 'priority', 'priority');
+
+ $query->orderBy($name_field);
+ $result = $query->execute();
+
+ $num_records = 0;
+ $last_name = 0;
+
+ foreach ($result as $record) {
+ $num_records++;
+ $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, 'Results returned in correct order.');
+ $last_priority = $record->$name_field;
+ }
+
+ $this->assertEqual($num_records, 8, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test GROUP BY clauses.
+ */
+ function testGroupBy() {
+ $query = db_select('test_task', 't');
+ $count_field = $query->addExpression('COUNT(task)', 'num');
+ $task_field = $query->addField('t', 'task');
+ $query->orderBy($count_field);
+ $query->groupBy($task_field);
+ $result = $query->execute();
+
+ $num_records = 0;
+ $last_count = 0;
+ $records = array();
+ foreach ($result as $record) {
+ $num_records++;
+ $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.');
+ $last_count = $record->$count_field;
+ $records[$record->$task_field] = $record->$count_field;
+ }
+
+ $correct_results = array(
+ 'eat' => 1,
+ 'sleep' => 2,
+ 'code' => 1,
+ 'found new band' => 1,
+ 'perform at superbowl' => 1,
+ );
+
+ foreach ($correct_results as $task => $count) {
+ $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task)));
+ }
+
+ $this->assertEqual($num_records, 6, 'Returned the correct number of total rows.');
+ }
+
+ /**
+ * Test GROUP BY and HAVING clauses together.
+ */
+ function testGroupByAndHaving() {
+ $query = db_select('test_task', 't');
+ $count_field = $query->addExpression('COUNT(task)', 'num');
+ $task_field = $query->addField('t', 'task');
+ $query->orderBy($count_field);
+ $query->groupBy($task_field);
+ $query->having('COUNT(task) >= 2');
+ $result = $query->execute();
+
+ $num_records = 0;
+ $last_count = 0;
+ $records = array();
+ foreach ($result as $record) {
+ $num_records++;
+ $this->assertTrue($record->$count_field >= 2, 'Record has the minimum count.');
+ $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.');
+ $last_count = $record->$count_field;
+ $records[$record->$task_field] = $record->$count_field;
+ }
+
+ $correct_results = array(
+ 'sleep' => 2,
+ );
+
+ foreach ($correct_results as $task => $count) {
+ $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task)));
+ }
+
+ $this->assertEqual($num_records, 1, 'Returned the correct number of total rows.');
+ }
+
+ /**
+ * Test range queries. The SQL clause varies with the database.
+ */
+ function testRange() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $query->range(0, 2);
+ $result = $query->execute();
+
+ $num_records = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ }
+
+ $this->assertEqual($num_records, 2, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test distinct queries.
+ */
+ function testDistinct() {
+ $query = db_select('test_task');
+ $task_field = $query->addField('test_task', 'task');
+ $query->distinct();
+ $result = $query->execute();
+
+ $num_records = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ }
+
+ $this->assertEqual($num_records, 6, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test that we can generate a count query from a built query.
+ */
+ function testCountQuery() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $query->orderBy('name');
+
+ $count = $query->countQuery()->execute()->fetchField();
+
+ $this->assertEqual($count, 4, 'Counted the correct number of records.');
+
+ // Now make sure we didn't break the original query! We should still have
+ // all of the fields we asked for.
+ $record = $query->execute()->fetch();
+ $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.');
+ $this->assertEqual($record->$age_field, 27, 'Correct data retrieved.');
+ }
+
+ function testHavingCountQuery() {
+ $query = db_select('test')
+ ->extend('PagerDefault')
+ ->groupBy('age')
+ ->having('age + 1 > 0');
+ $query->addField('test', 'age');
+ $query->addExpression('age + 1');
+ $count = count($query->execute()->fetchCol());
+ $this->assertEqual($count, 4, 'Counted the correct number of records.');
+ }
+
+ /**
+ * Test that countQuery properly removes 'all_fields' statements and
+ * ordering clauses.
+ */
+ function testCountQueryRemovals() {
+ $query = db_select('test');
+ $query->fields('test');
+ $query->orderBy('name');
+ $count = $query->countQuery();
+
+ // Check that the 'all_fields' statement is handled properly.
+ $tables = $query->getTables();
+ $this->assertEqual($tables['test']['all_fields'], 1, 'Query correctly sets \'all_fields\' statement.');
+ $tables = $count->getTables();
+ $this->assertFalse(isset($tables['test']['all_fields']), 'Count query correctly unsets \'all_fields\' statement.');
+
+ // Check that the ordering clause is handled properly.
+ $orderby = $query->getOrderBy();
+ $this->assertEqual($orderby['name'], 'ASC', 'Query correctly sets ordering clause.');
+ $orderby = $count->getOrderBy();
+ $this->assertFalse(isset($orderby['name']), 'Count query correctly unsets ordering caluse.');
+
+ // Make sure that the count query works.
+ $count = $count->execute()->fetchField();
+
+ $this->assertEqual($count, 4, 'Counted the correct number of records.');
+ }
+
+
+ /**
+ * Test that countQuery properly removes fields and expressions.
+ */
+ function testCountQueryFieldRemovals() {
+ // countQuery should remove all fields and expressions, so this can be
+ // tested by adding a non-existent field and expression: if it ends
+ // up in the query, an error will be thrown. If not, it will return the
+ // number of records, which in this case happens to be 4 (there are four
+ // records in the {test} table).
+ $query = db_select('test');
+ $query->fields('test', array('fail'));
+ $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed fields');
+
+ $query = db_select('test');
+ $query->addExpression('fail');
+ $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed expressions');
+ }
+
+ /**
+ * Test that we can generate a count query from a query with distinct.
+ */
+ function testCountQueryDistinct() {
+ $query = db_select('test_task');
+ $task_field = $query->addField('test_task', 'task');
+ $query->distinct();
+
+ $count = $query->countQuery()->execute()->fetchField();
+
+ $this->assertEqual($count, 6, 'Counted the correct number of records.');
+ }
+
+ /**
+ * Test that we can generate a count query from a query with GROUP BY.
+ */
+ function testCountQueryGroupBy() {
+ $query = db_select('test_task');
+ $pid_field = $query->addField('test_task', 'pid');
+ $query->groupBy('pid');
+
+ $count = $query->countQuery()->execute()->fetchField();
+
+ $this->assertEqual($count, 3, 'Counted the correct number of records.');
+
+ // Use a column alias as, without one, the query can succeed for the wrong
+ // reason.
+ $query = db_select('test_task');
+ $pid_field = $query->addField('test_task', 'pid', 'pid_alias');
+ $query->addExpression('COUNT(test_task.task)', 'count');
+ $query->groupBy('pid_alias');
+ $query->orderBy('pid_alias', 'asc');
+
+ $count = $query->countQuery()->execute()->fetchField();
+
+ $this->assertEqual($count, 3, 'Counted the correct number of records.');
+ }
+
+ /**
+ * Confirm that we can properly nest conditional clauses.
+ */
+ function testNestedConditions() {
+ // This query should translate to:
+ // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)"
+ // That should find only one record. Yes it's a non-optimal way of writing
+ // that query but that's not the point!
+ $query = db_select('test');
+ $query->addField('test', 'job');
+ $query->condition('name', 'Paul');
+ $query->condition(db_or()->condition('age', 26)->condition('age', 27));
+
+ $job = $query->execute()->fetchField();
+ $this->assertEqual($job, 'Songwriter', 'Correct data retrieved.');
+ }
+
+ /**
+ * Confirm we can join on a single table twice with a dynamic alias.
+ */
+ function testJoinTwice() {
+ $query = db_select('test')->fields('test');
+ $alias = $query->join('test', 'test', 'test.job = %alias.job');
+ $query->addField($alias, 'name', 'othername');
+ $query->addField($alias, 'job', 'otherjob');
+ $query->where("$alias.name <> test.name");
+ $crowded_job = $query->execute()->fetch();
+ $this->assertEqual($crowded_job->job, $crowded_job->otherjob, 'Correctly joined same table twice.');
+ $this->assertNotEqual($crowded_job->name, $crowded_job->othername, 'Correctly joined same table twice.');
+ }
+
+}
+
+/**
+ * Test more complex select statements, part 2.
+ */
+class DatabaseSelectComplexTestCase2 extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Select tests, complex 2',
+ 'description' => 'Test the Select query builder with even more complex queries.',
+ 'group' => 'Database',
+ );
+ }
+
+ function setUp() {
+ DrupalWebTestCase::setUp('database_test', 'node_access_test');
+
+ $schema['test'] = drupal_get_schema('test');
+ $schema['test_people'] = drupal_get_schema('test_people');
+ $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
+ $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
+ $schema['test_task'] = drupal_get_schema('test_task');
+
+ $this->installTables($schema);
+
+ $this->addSampleData();
+ }
+
+ /**
+ * Test that we can join on a query.
+ */
+ function testJoinSubquery() {
+ $acct = $this->drupalCreateUser(array('access content'));
+ $this->drupalLogin($acct);
+
+ $query = db_select('test_task', 'tt', array('target' => 'slave'));
+ $query->addExpression('tt.pid + 1', 'abc');
+ $query->condition('priority', 1, '>');
+ $query->condition('priority', 100, '<');
+
+ $subquery = db_select('test', 'tp');
+ $subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id');
+ $subquery->join('node', 'n', 'tp.id = n.nid');
+ $subquery->addTag('node_access');
+ $subquery->addMetaData('account', $acct);
+ $subquery->addField('tp', 'id');
+ $subquery->condition('age', 5, '>');
+ $subquery->condition('age', 500, '<');
+
+ $query->leftJoin($subquery, 'sq', 'tt.pid = sq.id');
+ $query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id');
+
+ // Construct the query string.
+ // This is the same sequence that SelectQuery::execute() goes through.
+ $query->preExecute();
+ $query->getArguments();
+ $str = (string) $query;
+
+ // Verify that the string only has one copy of condition placeholder 0.
+ $pos = strpos($str, 'db_condition_placeholder_0', 0);
+ $pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1);
+ $this->assertFalse($pos2, 'Condition placeholder is not repeated.');
+ }
+}
+
+class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Pager query tests',
+ 'description' => 'Test the pager query extender.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that a pager query returns the correct results.
+ *
+ * Note that we have to make an HTTP request to a test page handler
+ * because the pager depends on GET parameters.
+ */
+ function testEvenPagerQuery() {
+ // To keep the test from being too brittle, we determine up front
+ // what the page count should be dynamically, and pass the control
+ // information forward to the actual query on the other side of the
+ // HTTP request.
+ $limit = 2;
+ $count = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+ $correct_number = $limit;
+ $num_pages = floor($count / $limit);
+
+ // If there is no remainder from rounding, subtract 1 since we index from 0.
+ if (!($num_pages * $limit < $count)) {
+ $num_pages--;
+ }
+
+ for ($page = 0; $page <= $num_pages; ++$page) {
+ $this->drupalGet('database_test/pager_query_even/' . $limit, array('query' => array('page' => $page)));
+ $data = json_decode($this->drupalGetContent());
+
+ if ($page == $num_pages) {
+ $correct_number = $count - ($limit * $page);
+ }
+
+ $this->assertEqual(count($data->names), $correct_number, format_string('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
+ }
+ }
+
+ /**
+ * Confirm that a pager query returns the correct results.
+ *
+ * Note that we have to make an HTTP request to a test page handler
+ * because the pager depends on GET parameters.
+ */
+ function testOddPagerQuery() {
+ // To keep the test from being too brittle, we determine up front
+ // what the page count should be dynamically, and pass the control
+ // information forward to the actual query on the other side of the
+ // HTTP request.
+ $limit = 2;
+ $count = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
+
+ $correct_number = $limit;
+ $num_pages = floor($count / $limit);
+
+ // If there is no remainder from rounding, subtract 1 since we index from 0.
+ if (!($num_pages * $limit < $count)) {
+ $num_pages--;
+ }
+
+ for ($page = 0; $page <= $num_pages; ++$page) {
+ $this->drupalGet('database_test/pager_query_odd/' . $limit, array('query' => array('page' => $page)));
+ $data = json_decode($this->drupalGetContent());
+
+ if ($page == $num_pages) {
+ $correct_number = $count - ($limit * $page);
+ }
+
+ $this->assertEqual(count($data->names), $correct_number, format_string('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
+ }
+ }
+
+ /**
+ * Confirm that a pager query with inner pager query returns valid results.
+ *
+ * This is a regression test for #467984.
+ */
+ function testInnerPagerQuery() {
+ $query = db_select('test', 't')->extend('PagerDefault');
+ $query
+ ->fields('t', array('age'))
+ ->orderBy('age')
+ ->limit(5);
+
+ $outer_query = db_select($query);
+ $outer_query->addField('subquery', 'age');
+
+ $ages = $outer_query
+ ->execute()
+ ->fetchCol();
+ $this->assertEqual($ages, array(25, 26, 27, 28), 'Inner pager query returned the correct ages.');
+ }
+
+ /**
+ * Confirm that a paging query with a having expression returns valid results.
+ *
+ * This is a regression test for #467984.
+ */
+ function testHavingPagerQuery() {
+ $query = db_select('test', 't')->extend('PagerDefault');
+ $query
+ ->fields('t', array('name'))
+ ->orderBy('name')
+ ->groupBy('name')
+ ->having('MAX(age) > :count', array(':count' => 26))
+ ->limit(5);
+
+ $ages = $query
+ ->execute()
+ ->fetchCol();
+ $this->assertEqual($ages, array('George', 'Ringo'), 'Pager query with having expression returned the correct ages.');
+ }
+
+ /**
+ * Confirm that every pager gets a valid non-overlaping element ID.
+ */
+ function testElementNumbers() {
+ $_GET['page'] = '3, 2, 1, 0';
+
+ $name = db_select('test', 't')->extend('PagerDefault')
+ ->element(2)
+ ->fields('t', array('name'))
+ ->orderBy('age')
+ ->limit(1)
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($name, 'Paul', 'Pager query #1 with a specified element ID returned the correct results.');
+
+ // Setting an element smaller than the previous one
+ // should not overwrite the pager $maxElement with a smaller value.
+ $name = db_select('test', 't')->extend('PagerDefault')
+ ->element(1)
+ ->fields('t', array('name'))
+ ->orderBy('age')
+ ->limit(1)
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($name, 'George', 'Pager query #2 with a specified element ID returned the correct results.');
+
+ $name = db_select('test', 't')->extend('PagerDefault')
+ ->fields('t', array('name'))
+ ->orderBy('age')
+ ->limit(1)
+ ->execute()
+ ->fetchField();
+ $this->assertEqual($name, 'John', 'Pager query #3 with a generated element ID returned the correct results.');
+
+ unset($_GET['page']);
+ }
+}
+
+
+class DatabaseSelectTableSortDefaultTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Tablesort query tests',
+ 'description' => 'Test the tablesort query extender.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that a tablesort query returns the correct results.
+ *
+ * Note that we have to make an HTTP request to a test page handler
+ * because the pager depends on GET parameters.
+ */
+ function testTableSortQuery() {
+ $sorts = array(
+ array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
+ array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
+ array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
+ array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
+ // more elements here
+
+ );
+
+ foreach ($sorts as $sort) {
+ $this->drupalGet('database_test/tablesort/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
+ $data = json_decode($this->drupalGetContent());
+
+ $first = array_shift($data->tasks);
+ $last = array_pop($data->tasks);
+
+ $this->assertEqual($first->task, $sort['first'], 'Items appear in the correct order.');
+ $this->assertEqual($last->task, $sort['last'], 'Items appear in the correct order.');
+ }
+ }
+
+ /**
+ * Confirm that if a tablesort's orderByHeader is called before another orderBy, that the header happens first.
+ *
+ */
+ function testTableSortQueryFirst() {
+ $sorts = array(
+ array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
+ array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
+ array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
+ array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
+ // more elements here
+
+ );
+
+ foreach ($sorts as $sort) {
+ $this->drupalGet('database_test/tablesort_first/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
+ $data = json_decode($this->drupalGetContent());
+
+ $first = array_shift($data->tasks);
+ $last = array_pop($data->tasks);
+
+ $this->assertEqual($first->task, $sort['first'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
+ $this->assertEqual($last->task, $sort['last'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
+ }
+ }
+
+ /**
+ * Confirm that if a sort is not set in a tableselect form there is no error thrown when using the default.
+ */
+ function testTableSortDefaultSort() {
+ $this->drupalGet('database_test/tablesort_default_sort');
+ // Any PHP errors or notices thrown would trigger a simpletest exception, so
+ // no additional assertions are needed.
+ }
+}
+
+/**
+ * Select tagging tests.
+ *
+ * Tags are a way to flag queries for alter hooks so they know
+ * what type of query it is, such as "node_access".
+ */
+class DatabaseTaggingTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Query tagging tests',
+ 'description' => 'Test the tagging capabilities of the Select builder.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that a query has a "tag" added to it.
+ */
+ function testHasTag() {
+ $query = db_select('test');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+
+ $query->addTag('test');
+
+ $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.');
+ $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.');
+ }
+
+ /**
+ * Test query tagging "has all of these tags" functionality.
+ */
+ function testHasAllTags() {
+ $query = db_select('test');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+
+ $query->addTag('test');
+ $query->addTag('other');
+
+ $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.');
+ $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.');
+ }
+
+ /**
+ * Test query tagging "has at least one of these tags" functionality.
+ */
+ function testHasAnyTag() {
+ $query = db_select('test');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+
+ $query->addTag('test');
+
+ $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.');
+ $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.');
+ }
+
+ /**
+ * Test that we can attach meta data to a query object.
+ *
+ * This is how we pass additional context to alter hooks.
+ */
+ function testMetaData() {
+ $query = db_select('test');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+
+ $data = array(
+ 'a' => 'A',
+ 'b' => 'B',
+ );
+
+ $query->addMetaData('test', $data);
+
+ $return = $query->getMetaData('test');
+ $this->assertEqual($data, $return, 'Corect metadata returned.');
+
+ $return = $query->getMetaData('nothere');
+ $this->assertNull($return, 'Non-existent key returned NULL.');
+ }
+}
+
+/**
+ * Select alter tests.
+ *
+ * @see database_test_query_alter()
+ */
+class DatabaseAlterTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Query altering tests',
+ 'description' => 'Test the hook_query_alter capabilities of the Select builder.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that we can do basic alters.
+ */
+ function testSimpleAlter() {
+ $query = db_select('test');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+ $query->addTag('database_test_alter_add_range');
+
+ $result = $query->execute();
+
+ $num_records = 0;
+ foreach ($result as $record) {
+ $num_records++;
+ }
+
+ $this->assertEqual($num_records, 2, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test that we can alter the joins on a query.
+ */
+ function testAlterWithJoin() {
+ $query = db_select('test_task');
+ $tid_field = $query->addField('test_task', 'tid');
+ $task_field = $query->addField('test_task', 'task');
+ $query->orderBy($task_field);
+ $query->addTag('database_test_alter_add_join');
+
+ $result = $query->execute();
+
+ $records = $result->fetchAll();
+
+ $this->assertEqual(count($records), 2, 'Returned the correct number of rows.');
+
+ $this->assertEqual($records[0]->name, 'George', 'Correct data retrieved.');
+ $this->assertEqual($records[0]->$tid_field, 4, 'Correct data retrieved.');
+ $this->assertEqual($records[0]->$task_field, 'sing', 'Correct data retrieved.');
+ $this->assertEqual($records[1]->name, 'George', 'Correct data retrieved.');
+ $this->assertEqual($records[1]->$tid_field, 5, 'Correct data retrieved.');
+ $this->assertEqual($records[1]->$task_field, 'sleep', 'Correct data retrieved.');
+ }
+
+ /**
+ * Test that we can alter a query's conditionals.
+ */
+ function testAlterChangeConditional() {
+ $query = db_select('test_task');
+ $tid_field = $query->addField('test_task', 'tid');
+ $pid_field = $query->addField('test_task', 'pid');
+ $task_field = $query->addField('test_task', 'task');
+ $people_alias = $query->join('test', 'people', "test_task.pid = people.id");
+ $name_field = $query->addField($people_alias, 'name', 'name');
+ $query->condition('test_task.tid', '1');
+ $query->orderBy($tid_field);
+ $query->addTag('database_test_alter_change_conditional');
+
+ $result = $query->execute();
+
+ $records = $result->fetchAll();
+
+ $this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
+ $this->assertEqual($records[0]->$name_field, 'John', 'Correct data retrieved.');
+ $this->assertEqual($records[0]->$tid_field, 2, 'Correct data retrieved.');
+ $this->assertEqual($records[0]->$pid_field, 1, 'Correct data retrieved.');
+ $this->assertEqual($records[0]->$task_field, 'sleep', 'Correct data retrieved.');
+ }
+
+ /**
+ * Test that we can alter the fields of a query.
+ */
+ function testAlterChangeFields() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addField('test', 'age', 'age');
+ $query->orderBy('name');
+ $query->addTag('database_test_alter_change_fields');
+
+ $record = $query->execute()->fetch();
+ $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.');
+ $this->assertFalse(isset($record->$age_field), 'Age field not found, as intended.');
+ }
+
+ /**
+ * Test that we can alter expressions in the query.
+ */
+ function testAlterExpression() {
+ $query = db_select('test');
+ $name_field = $query->addField('test', 'name');
+ $age_field = $query->addExpression("age*2", 'double_age');
+ $query->condition('age', 27);
+ $query->addTag('database_test_alter_change_expressions');
+ $result = $query->execute();
+
+ // Ensure that we got the right record.
+ $record = $result->fetch();
+
+ $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
+ $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.');
+ }
+
+ /**
+ * Test that we can remove a range() value from a query. This also tests hook_query_TAG_alter().
+ */
+ function testAlterRemoveRange() {
+ $query = db_select('test');
+ $query->addField('test', 'name');
+ $query->addField('test', 'age', 'age');
+ $query->range(0, 2);
+ $query->addTag('database_test_alter_remove_range');
+
+ $num_records = count($query->execute()->fetchAll());
+
+ $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
+ }
+
+ /**
+ * Test that we can do basic alters on subqueries.
+ */
+ function testSimpleAlterSubquery() {
+ // Create a sub-query with an alter tag.
+ $subquery = db_select('test', 'p');
+ $subquery->addField('p', 'name');
+ $subquery->addField('p', 'id');
+ // Pick out George.
+ $subquery->condition('age', 27);
+ $subquery->addExpression("age*2", 'double_age');
+ // This query alter should change it to age * 3.
+ $subquery->addTag('database_test_alter_change_expressions');
+
+ // Create a main query and join to sub-query.
+ $query = db_select('test_task', 'tt');
+ $query->join($subquery, 'pq', 'pq.id = tt.pid');
+ $age_field = $query->addField('pq', 'double_age');
+ $name_field = $query->addField('pq', 'name');
+
+ $record = $query->execute()->fetch();
+ $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
+ $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.');
+ }
+}
+
+/**
+ * Regression tests.
+ */
+class DatabaseRegressionTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Regression tests',
+ 'description' => 'Regression tests cases for the database layer.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Regression test for #310447.
+ *
+ * Tries to insert non-ascii UTF-8 data in a database column and checks
+ * if its stored properly.
+ */
+ function testRegression_310447() {
+ // That's a 255 character UTF-8 string.
+ $name = str_repeat("é", 255);
+ db_insert('test')
+ ->fields(array(
+ 'name' => $name,
+ 'age' => 20,
+ 'job' => 'Dancer',
+ ))->execute();
+
+ $from_database = db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
+ $this->assertIdentical($name, $from_database, "The database handles UTF-8 characters cleanly.");
+ }
+
+ /**
+ * Test the db_table_exists() function.
+ */
+ function testDBTableExists() {
+ $this->assertIdentical(TRUE, db_table_exists('node'), 'Returns true for existent table.');
+ $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), 'Returns false for nonexistent table.');
+ }
+
+ /**
+ * Test the db_field_exists() function.
+ */
+ function testDBFieldExists() {
+ $this->assertIdentical(TRUE, db_field_exists('node', 'nid'), 'Returns true for existent column.');
+ $this->assertIdentical(FALSE, db_field_exists('node', 'nosuchcolumn'), 'Returns false for nonexistent column.');
+ }
+
+ /**
+ * Test the db_index_exists() function.
+ */
+ function testDBIndexExists() {
+ $this->assertIdentical(TRUE, db_index_exists('node', 'node_created'), 'Returns true for existent index.');
+ $this->assertIdentical(FALSE, db_index_exists('node', 'nosuchindex'), 'Returns false for nonexistent index.');
+ }
+}
+
+/**
+ * Query logging tests.
+ */
+class DatabaseLoggingTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Query logging',
+ 'description' => 'Test the query logging facility.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that we can log the existence of a query.
+ */
+ function testEnableLogging() {
+ $log = Database::startLog('testing');
+
+ db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+ db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
+
+ // Trigger a call that does not have file in the backtrace.
+ call_user_func_array('db_query', array('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo')))->fetchCol();
+
+ $queries = Database::getLog('testing', 'default');
+
+ $this->assertEqual(count($queries), 3, 'Correct number of queries recorded.');
+
+ foreach ($queries as $query) {
+ $this->assertEqual($query['caller']['function'], __FUNCTION__, 'Correct function in query log.');
+ }
+ }
+
+ /**
+ * Test that we can run two logs in parallel.
+ */
+ function testEnableMultiLogging() {
+ Database::startLog('testing1');
+
+ db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+
+ Database::startLog('testing2');
+
+ db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
+
+ $queries1 = Database::getLog('testing1');
+ $queries2 = Database::getLog('testing2');
+
+ $this->assertEqual(count($queries1), 2, 'Correct number of queries recorded for log 1.');
+ $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for log 2.');
+ }
+
+ /**
+ * Test that we can log queries against multiple targets on the same connection.
+ */
+ function testEnableTargetLogging() {
+ // Clone the master credentials to a slave connection and to another fake
+ // connection.
+ $connection_info = Database::getConnectionInfo('default');
+ Database::addConnectionInfo('default', 'slave', $connection_info['default']);
+
+ Database::startLog('testing1');
+
+ db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+
+ db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'));//->fetchCol();
+
+ $queries1 = Database::getLog('testing1');
+
+ $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.');
+ $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.');
+ $this->assertEqual($queries1[1]['target'], 'slave', 'Second query used slave target.');
+ }
+
+ /**
+ * Test that logs to separate targets collapse to the same connection properly.
+ *
+ * This test is identical to the one above, except that it doesn't create
+ * a fake target so the query should fall back to running on the default
+ * target.
+ */
+ function testEnableTargetLoggingNoTarget() {
+ Database::startLog('testing1');
+
+ db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+
+ // We use "fake" here as a target because any non-existent target will do.
+ // However, because all of the tests in this class share a single page
+ // request there is likely to be a target of "slave" from one of the other
+ // unit tests, so we use a target here that we know with absolute certainty
+ // does not exist.
+ db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol();
+
+ $queries1 = Database::getLog('testing1');
+
+ $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.');
+ $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.');
+ $this->assertEqual($queries1[1]['target'], 'default', 'Second query used default target as fallback.');
+ }
+
+ /**
+ * Test that we can log queries separately on different connections.
+ */
+ function testEnableMultiConnectionLogging() {
+ // Clone the master credentials to a fake connection.
+ // That both connections point to the same physical database is irrelevant.
+ $connection_info = Database::getConnectionInfo('default');
+ Database::addConnectionInfo('test2', 'default', $connection_info['default']);
+
+ Database::startLog('testing1');
+ Database::startLog('testing1', 'test2');
+
+ db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
+
+ $old_key = db_set_active('test2');
+
+ db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'))->fetchCol();
+
+ db_set_active($old_key);
+
+ $queries1 = Database::getLog('testing1');
+ $queries2 = Database::getLog('testing1', 'test2');
+
+ $this->assertEqual(count($queries1), 1, 'Correct number of queries recorded for first connection.');
+ $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for second connection.');
+ }
+}
+
+/**
+ * Query serialization tests.
+ */
+class DatabaseSerializeQueryTestCase extends DatabaseTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Serialize query',
+ 'description' => 'Test serializing and unserializing a query.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Confirm that a query can be serialized and unserialized.
+ */
+ function testSerializeQuery() {
+ $query = db_select('test');
+ $query->addField('test', 'age');
+ $query->condition('name', 'Ringo');
+ // If this doesn't work, it will throw an exception, so no need for an
+ // assertion.
+ $query = unserialize(serialize($query));
+ $results = $query->execute()->fetchCol();
+ $this->assertEqual($results[0], 28, 'Query properly executed after unserialization.');
+ }
+}
+
+/**
+ * Range query tests.
+ */
+class DatabaseRangeQueryTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Range query test',
+ 'description' => 'Test the Range query functionality.',
+ 'group' => 'Database',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('database_test');
+ }
+
+ /**
+ * Confirm that range query work and return correct result.
+ */
+ function testRangeQuery() {
+ // Test if return correct number of rows.
+ $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll();
+ $this->assertEqual(count($range_rows), 3, 'Range query work and return correct number of rows.');
+
+ // Test if return target data.
+ $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll();
+ $raw_rows = array_slice($raw_rows, 2, 3);
+ $this->assertEqual($range_rows, $raw_rows, 'Range query work and return target data.');
+ }
+}
+
+/**
+ * Temporary query tests.
+ */
+class DatabaseTemporaryQueryTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Temporary query test',
+ 'description' => 'Test the temporary query functionality.',
+ 'group' => 'Database',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('database_test');
+ }
+
+ /**
+ * Return the number of rows of a table.
+ */
+ function countTableRows($table_name) {
+ return db_select($table_name)->countQuery()->execute()->fetchField();
+ }
+
+ /**
+ * Confirm that temporary tables work and are limited to one request.
+ */
+ function testTemporaryQuery() {
+ $this->drupalGet('database_test/db_query_temporary');
+ $data = json_decode($this->drupalGetContent());
+ if ($data) {
+ $this->assertEqual($this->countTableRows("system"), $data->row_count, 'The temporary table contains the correct amount of rows.');
+ $this->assertFalse(db_table_exists($data->table_name), 'The temporary table is, indeed, temporary.');
+ }
+ else {
+ $this->fail("The creation of the temporary table failed.");
+ }
+
+ // Now try to run two db_query_temporary() in the same request.
+ $table_name_system = db_query_temporary('SELECT status FROM {system}', array());
+ $table_name_users = db_query_temporary('SELECT uid FROM {users}', array());
+
+ $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), 'A temporary table was created successfully in this request.');
+ $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), 'A second temporary table was created successfully in this request.');
+ }
+}
+
+/**
+ * Test how the current database driver interprets the SQL syntax.
+ *
+ * In order to ensure consistent SQL handling throughout Drupal
+ * across multiple kinds of database systems, we test that the
+ * database system interprets SQL syntax in an expected fashion.
+ */
+class DatabaseBasicSyntaxTestCase extends DatabaseTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Basic SQL syntax tests',
+ 'description' => 'Test SQL syntax interpretation.',
+ 'group' => 'Database',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('database_test');
+ }
+
+ /**
+ * Test for string concatenation.
+ */
+ function testBasicConcat() {
+ $result = db_query('SELECT CONCAT(:a1, CONCAT(:a2, CONCAT(:a3, CONCAT(:a4, :a5))))', array(
+ ':a1' => 'This',
+ ':a2' => ' ',
+ ':a3' => 'is',
+ ':a4' => ' a ',
+ ':a5' => 'test.',
+ ));
+ $this->assertIdentical($result->fetchField(), 'This is a test.', 'Basic CONCAT works.');
+ }
+
+ /**
+ * Test for string concatenation with field values.
+ */
+ function testFieldConcat() {
+ $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array(
+ ':a1' => 'The age of ',
+ ':a2' => ' is ',
+ ':a3' => '.',
+ ':age' => 25,
+ ));
+ $this->assertIdentical($result->fetchField(), 'The age of John is 25.', 'Field CONCAT works.');
+ }
+
+ /**
+ * Test escaping of LIKE wildcards.
+ */
+ function testLikeEscape() {
+ db_insert('test')
+ ->fields(array(
+ 'name' => 'Ring_',
+ ))
+ ->execute();
+
+ // Match both "Ringo" and "Ring_".
+ $num_matches = db_select('test', 't')
+ ->condition('name', 'Ring_', 'LIKE')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertIdentical($num_matches, '2', 'Found 2 records.');
+ // Match only "Ring_" using a LIKE expression with no wildcards.
+ $num_matches = db_select('test', 't')
+ ->condition('name', db_like('Ring_'), 'LIKE')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertIdentical($num_matches, '1', 'Found 1 record.');
+ }
+
+ /**
+ * Test LIKE query containing a backslash.
+ */
+ function testLikeBackslash() {
+ db_insert('test')
+ ->fields(array('name'))
+ ->values(array(
+ 'name' => 'abcde\f',
+ ))
+ ->values(array(
+ 'name' => 'abc%\_',
+ ))
+ ->execute();
+
+ // Match both rows using a LIKE expression with two wildcards and a verbatim
+ // backslash.
+ $num_matches = db_select('test', 't')
+ ->condition('name', 'abc%\\\\_', 'LIKE')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertIdentical($num_matches, '2', 'Found 2 records.');
+ // Match only the former using a LIKE expression with no wildcards.
+ $num_matches = db_select('test', 't')
+ ->condition('name', db_like('abc%\_'), 'LIKE')
+ ->countQuery()
+ ->execute()
+ ->fetchField();
+ $this->assertIdentical($num_matches, '1', 'Found 1 record.');
+ }
+}
+
+/**
+ * Test case sensitivity handling.
+ */
+class DatabaseCaseSensitivityTestCase extends DatabaseTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Case sensitivity',
+ 'description' => 'Test handling case sensitive collation.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test BINARY collation in MySQL.
+ */
+ function testCaseSensitiveInsert() {
+ $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+ $john = db_insert('test')
+ ->fields(array(
+ 'name' => 'john', // <- A record already exists with name 'John'.
+ 'age' => 2,
+ 'job' => 'Baby',
+ ))
+ ->execute();
+
+ $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
+ $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'john'))->fetchField();
+ $this->assertIdentical($saved_age, '2', 'Can retrieve after inserting.');
+ }
+}
+
+/**
+ * Test invalid data handling.
+ */
+class DatabaseInvalidDataTestCase extends DatabaseTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Invalid data',
+ 'description' => 'Test handling of some invalid data.',
+ 'group' => 'Database',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('database_test');
+ }
+
+ /**
+ * Traditional SQL database systems abort inserts when invalid data is encountered.
+ */
+ function testInsertDuplicateData() {
+ // Try to insert multiple records where at least one has bad data.
+ try {
+ db_insert('test')
+ ->fields(array('name', 'age', 'job'))
+ ->values(array(
+ 'name' => 'Elvis',
+ 'age' => 63,
+ 'job' => 'Singer',
+ ))->values(array(
+ 'name' => 'John', // <-- Duplicate value on unique field.
+ 'age' => 17,
+ 'job' => 'Consultant',
+ ))
+ ->values(array(
+ 'name' => 'Frank',
+ 'age' => 75,
+ 'job' => 'Singer',
+ ))
+ ->execute();
+ $this->fail('Insert succeedded when it should not have.');
+ }
+ catch (Exception $e) {
+ // Check if the first record was inserted.
+ $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField();
+
+ if ($name == 'Elvis') {
+ if (!Database::getConnection()->supportsTransactions()) {
+ // This is an expected fail.
+ // Database engines that don't support transactions can leave partial
+ // inserts in place when an error occurs. This is the case for MySQL
+ // when running on a MyISAM table.
+ $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions");
+ }
+ else {
+ $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.');
+ }
+ }
+ else {
+ $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.');
+ }
+
+ // Ensure the other values were not inserted.
+ $record = db_select('test')
+ ->fields('test', array('name', 'age'))
+ ->condition('age', array(17, 75), 'IN')
+ ->execute()->fetchObject();
+
+ $this->assertFalse($record, 'The rest of the insert aborted as expected.');
+ }
+ }
+
+}
+
+/**
+ * Drupal-specific SQL syntax tests.
+ */
+class DatabaseQueryTestCase extends DatabaseTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Custom query syntax tests',
+ 'description' => 'Test Drupal\'s extended prepared statement syntax..',
+ 'group' => 'Database',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('database_test');
+ }
+
+ /**
+ * Test that we can specify an array of values in the query by simply passing in an array.
+ */
+ function testArraySubstitution() {
+ $names = db_query('SELECT name FROM {test} WHERE age IN (:ages) ORDER BY age', array(':ages' => array(25, 26, 27)))->fetchAll();
+
+ $this->assertEqual(count($names), 3, 'Correct number of names returned');
+ }
+}
+
+/**
+ * Test transaction support, particularly nesting.
+ *
+ * We test nesting by having two transaction layers, an outer and inner. The
+ * outer layer encapsulates the inner layer. Our transaction nesting abstraction
+ * should allow the outer layer function to call any function it wants,
+ * especially the inner layer that starts its own transaction, and be
+ * confident that, when the function it calls returns, its own transaction
+ * is still "alive."
+ *
+ * Call structure:
+ * transactionOuterLayer()
+ * Start transaction
+ * transactionInnerLayer()
+ * Start transaction (does nothing in database)
+ * [Maybe decide to roll back]
+ * Do more stuff
+ * Should still be in transaction A
+ *
+ */
+class DatabaseTransactionTestCase extends DatabaseTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Transaction tests',
+ 'description' => 'Test the transaction abstraction system.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Helper method for transaction unit test. This "outer layer" transaction
+ * starts and then encapsulates the "inner layer" transaction. This nesting
+ * is used to evaluate whether the the database transaction API properly
+ * supports nesting. By "properly supports," we mean the outer transaction
+ * continues to exist regardless of what functions are called and whether
+ * those functions start their own transactions.
+ *
+ * In contrast, a typical database would commit the outer transaction, start
+ * a new transaction for the inner layer, commit the inner layer transaction,
+ * and then be confused when the outer layer transaction tries to commit its
+ * transaction (which was already committed when the inner transaction
+ * started).
+ *
+ * @param $suffix
+ * Suffix to add to field values to differentiate tests.
+ * @param $rollback
+ * Whether or not to try rolling back the transaction when we're done.
+ * @param $ddl_statement
+ * Whether to execute a DDL statement during the inner transaction.
+ */
+ protected function transactionOuterLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) {
+ $connection = Database::getConnection();
+ $depth = $connection->transactionDepth();
+ $txn = db_transaction();
+
+ // Insert a single row into the testing table.
+ db_insert('test')
+ ->fields(array(
+ 'name' => 'David' . $suffix,
+ 'age' => '24',
+ ))
+ ->execute();
+
+ $this->assertTrue($connection->inTransaction(), 'In transaction before calling nested transaction.');
+
+ // We're already in a transaction, but we call ->transactionInnerLayer
+ // to nest another transaction inside the current one.
+ $this->transactionInnerLayer($suffix, $rollback, $ddl_statement);
+
+ $this->assertTrue($connection->inTransaction(), 'In transaction after calling nested transaction.');
+
+ if ($rollback) {
+ // Roll back the transaction, if requested.
+ // This rollback should propagate to the last savepoint.
+ $txn->rollback();
+ $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().');
+ }
+ }
+
+ /**
+ * Helper method for transaction unit tests. This "inner layer" transaction
+ * is either used alone or nested inside of the "outer layer" transaction.
+ *
+ * @param $suffix
+ * Suffix to add to field values to differentiate tests.
+ * @param $rollback
+ * Whether or not to try rolling back the transaction when we're done.
+ * @param $ddl_statement
+ * Whether to execute a DDL statement during the transaction.
+ */
+ protected function transactionInnerLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) {
+ $connection = Database::getConnection();
+
+ $depth = $connection->transactionDepth();
+ // Start a transaction. If we're being called from ->transactionOuterLayer,
+ // then we're already in a transaction. Normally, that would make starting
+ // a transaction here dangerous, but the database API handles this problem
+ // for us by tracking the nesting and avoiding the danger.
+ $txn = db_transaction();
+
+ $depth2 = $connection->transactionDepth();
+ $this->assertTrue($depth < $depth2, 'Transaction depth is has increased with new transaction.');
+
+ // Insert a single row into the testing table.
+ db_insert('test')
+ ->fields(array(
+ 'name' => 'Daniel' . $suffix,
+ 'age' => '19',
+ ))
+ ->execute();
+
+ $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.');
+
+ if ($ddl_statement) {
+ $table = array(
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('id'),
+ );
+ db_create_table('database_test_1', $table);
+
+ $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.');
+ }
+
+ if ($rollback) {
+ // Roll back the transaction, if requested.
+ // This rollback should propagate to the last savepoint.
+ $txn->rollback();
+ $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().');
+ }
+ }
+
+ /**
+ * Test transaction rollback on a database that supports transactions.
+ *
+ * If the active connection does not support transactions, this test does nothing.
+ */
+ function testTransactionRollBackSupported() {
+ // This test won't work right if transactions are not supported.
+ if (!Database::getConnection()->supportsTransactions()) {
+ return;
+ }
+ try {
+ // Create two nested transactions. Roll back from the inner one.
+ $this->transactionOuterLayer('B', TRUE);
+
+ // Neither of the rows we inserted in the two transaction layers
+ // should be present in the tables post-rollback.
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
+ $this->assertNotIdentical($saved_age, '24', 'Cannot retrieve DavidB row after commit.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
+ $this->assertNotIdentical($saved_age, '19', 'Cannot retrieve DanielB row after commit.');
+ }
+ catch (Exception $e) {
+ $this->fail($e->getMessage());
+ }
+ }
+
+ /**
+ * Test transaction rollback on a database that does not support transactions.
+ *
+ * If the active driver supports transactions, this test does nothing.
+ */
+ function testTransactionRollBackNotSupported() {
+ // This test won't work right if transactions are supported.
+ if (Database::getConnection()->supportsTransactions()) {
+ return;
+ }
+ try {
+ // Create two nested transactions. Attempt to roll back from the inner one.
+ $this->transactionOuterLayer('B', TRUE);
+
+ // Because our current database claims to not support transactions,
+ // the inserted rows should be present despite the attempt to roll back.
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
+ $this->assertIdentical($saved_age, '24', 'DavidB not rolled back, since transactions are not supported.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
+ $this->assertIdentical($saved_age, '19', 'DanielB not rolled back, since transactions are not supported.');
+ }
+ catch (Exception $e) {
+ $this->fail($e->getMessage());
+ }
+ }
+
+ /**
+ * Test committed transaction.
+ *
+ * The behavior of this test should be identical for connections that support
+ * transactions and those that do not.
+ */
+ function testCommittedTransaction() {
+ try {
+ // Create two nested transactions. The changes should be committed.
+ $this->transactionOuterLayer('A');
+
+ // Because we committed, both of the inserted rows should be present.
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField();
+ $this->assertIdentical($saved_age, '24', 'Can retrieve DavidA row after commit.');
+ $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField();
+ $this->assertIdentical($saved_age, '19', 'Can retrieve DanielA row after commit.');
+ }
+ catch (Exception $e) {
+ $this->fail($e->getMessage());
+ }
+ }
+
+ /**
+ * Test the compatibility of transactions with DDL statements.
+ */
+ function testTransactionWithDdlStatement() {
+ // First, test that a commit works normally, even with DDL statements.
+ $transaction = db_transaction();
+ $this->insertRow('row');
+ $this->executeDDLStatement();
+ unset($transaction);
+ $this->assertRowPresent('row');
+
+ // Even in different order.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->executeDDLStatement();
+ $this->insertRow('row');
+ unset($transaction);
+ $this->assertRowPresent('row');
+
+ // Even with stacking.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $transaction2 = db_transaction();
+ $this->executeDDLStatement();
+ unset($transaction2);
+ $transaction3 = db_transaction();
+ $this->insertRow('row');
+ unset($transaction3);
+ unset($transaction);
+ $this->assertRowPresent('row');
+
+ // A transaction after a DDL statement should still work the same.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $transaction2 = db_transaction();
+ $this->executeDDLStatement();
+ unset($transaction2);
+ $transaction3 = db_transaction();
+ $this->insertRow('row');
+ $transaction3->rollback();
+ unset($transaction3);
+ unset($transaction);
+ $this->assertRowAbsent('row');
+
+ // The behavior of a rollback depends on the type of database server.
+ if (Database::getConnection()->supportsTransactionalDDL()) {
+ // For database servers that support transactional DDL, a rollback
+ // of a transaction including DDL statements should be possible.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->insertRow('row');
+ $this->executeDDLStatement();
+ $transaction->rollback();
+ unset($transaction);
+ $this->assertRowAbsent('row');
+
+ // Including with stacking.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $transaction2 = db_transaction();
+ $this->executeDDLStatement();
+ unset($transaction2);
+ $transaction3 = db_transaction();
+ $this->insertRow('row');
+ unset($transaction3);
+ $transaction->rollback();
+ unset($transaction);
+ $this->assertRowAbsent('row');
+ }
+ else {
+ // For database servers that do not support transactional DDL,
+ // the DDL statement should commit the transaction stack.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->insertRow('row');
+ $this->executeDDLStatement();
+ // Rollback the outer transaction.
+ try {
+ $transaction->rollback();
+ unset($transaction);
+ // @TODO: an exception should be triggered here, but is not, because
+ // "ROLLBACK" fails silently in MySQL if there is no transaction active.
+ // $this->fail(t('Rolling back a transaction containing DDL should fail.'));
+ }
+ catch (DatabaseTransactionNoActiveException $e) {
+ $this->pass('Rolling back a transaction containing DDL should fail.');
+ }
+ $this->assertRowPresent('row');
+ }
+ }
+
+ /**
+ * Insert a single row into the testing table.
+ */
+ protected function insertRow($name) {
+ db_insert('test')
+ ->fields(array(
+ 'name' => $name,
+ ))
+ ->execute();
+ }
+
+ /**
+ * Execute a DDL statement.
+ */
+ protected function executeDDLStatement() {
+ static $count = 0;
+ $table = array(
+ 'fields' => array(
+ 'id' => array(
+ 'type' => 'serial',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('id'),
+ );
+ db_create_table('database_test_' . ++$count, $table);
+ }
+
+ /**
+ * Start over for a new test.
+ */
+ protected function cleanUp() {
+ db_truncate('test')
+ ->execute();
+ }
+
+ /**
+ * Assert that a given row is present in the test table.
+ *
+ * @param $name
+ * The name of the row.
+ * @param $message
+ * The message to log for the assertion.
+ */
+ function assertRowPresent($name, $message = NULL) {
+ if (!isset($message)) {
+ $message = format_string('Row %name is present.', array('%name' => $name));
+ }
+ $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
+ return $this->assertTrue($present, $message);
+ }
+
+ /**
+ * Assert that a given row is absent from the test table.
+ *
+ * @param $name
+ * The name of the row.
+ * @param $message
+ * The message to log for the assertion.
+ */
+ function assertRowAbsent($name, $message = NULL) {
+ if (!isset($message)) {
+ $message = format_string('Row %name is absent.', array('%name' => $name));
+ }
+ $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
+ return $this->assertFalse($present, $message);
+ }
+
+ /**
+ * Test transaction stacking and commit / rollback.
+ */
+ function testTransactionStacking() {
+ // This test won't work right if transactions are not supported.
+ if (!Database::getConnection()->supportsTransactions()) {
+ return;
+ }
+
+ $database = Database::getConnection();
+
+ // Standard case: pop the inner transaction before the outer transaction.
+ $transaction = db_transaction();
+ $this->insertRow('outer');
+ $transaction2 = db_transaction();
+ $this->insertRow('inner');
+ // Pop the inner transaction.
+ unset($transaction2);
+ $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the inner transaction');
+ // Pop the outer transaction.
+ unset($transaction);
+ $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the outer transaction');
+ $this->assertRowPresent('outer');
+ $this->assertRowPresent('inner');
+
+ // Pop the transaction in a different order they have been pushed.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->insertRow('outer');
+ $transaction2 = db_transaction();
+ $this->insertRow('inner');
+ // Pop the outer transaction, nothing should happen.
+ unset($transaction);
+ $this->insertRow('inner-after-outer-commit');
+ $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
+ // Pop the inner transaction, the whole transaction should commit.
+ unset($transaction2);
+ $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
+ $this->assertRowPresent('outer');
+ $this->assertRowPresent('inner');
+ $this->assertRowPresent('inner-after-outer-commit');
+
+ // Rollback the inner transaction.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->insertRow('outer');
+ $transaction2 = db_transaction();
+ $this->insertRow('inner');
+ // Now rollback the inner transaction.
+ $transaction2->rollback();
+ unset($transaction2);
+ $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
+ // Pop the outer transaction, it should commit.
+ $this->insertRow('outer-after-inner-rollback');
+ unset($transaction);
+ $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
+ $this->assertRowPresent('outer');
+ $this->assertRowAbsent('inner');
+ $this->assertRowPresent('outer-after-inner-rollback');
+
+ // Rollback the inner transaction after committing the outer one.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->insertRow('outer');
+ $transaction2 = db_transaction();
+ $this->insertRow('inner');
+ // Pop the outer transaction, nothing should happen.
+ unset($transaction);
+ $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
+ // Now rollback the inner transaction, it should rollback.
+ $transaction2->rollback();
+ unset($transaction2);
+ $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
+ $this->assertRowPresent('outer');
+ $this->assertRowAbsent('inner');
+
+ // Rollback the outer transaction while the inner transaction is active.
+ // In that case, an exception will be triggered because we cannot
+ // ensure that the final result will have any meaning.
+ $this->cleanUp();
+ $transaction = db_transaction();
+ $this->insertRow('outer');
+ $transaction2 = db_transaction();
+ $this->insertRow('inner');
+ $transaction3 = db_transaction();
+ $this->insertRow('inner2');
+ // Rollback the outer transaction.
+ try {
+ $transaction->rollback();
+ unset($transaction);
+ $this->fail('Rolling back the outer transaction while the inner transaction is active resulted in an exception.');
+ }
+ catch (DatabaseTransactionOutOfOrderException $e) {
+ $this->pass('Rolling back the outer transaction while the inner transaction is active resulted in an exception.');
+ }
+ $this->assertFalse($database->inTransaction(), 'No more in a transaction after rolling back the outer transaction');
+ // Try to commit one inner transaction.
+ unset($transaction3);
+ $this->pass('Trying to commit an inner transaction resulted in an exception.');
+ // Try to rollback one inner transaction.
+ try {
+ $transaction->rollback();
+ unset($transaction2);
+ $this->fail('Trying to commit an inner transaction resulted in an exception.');
+ }
+ catch (DatabaseTransactionNoActiveException $e) {
+ $this->pass('Trying to commit an inner transaction resulted in an exception.');
+ }
+ $this->assertRowAbsent('outer');
+ $this->assertRowAbsent('inner');
+ $this->assertRowAbsent('inner2');
+ }
+}
+
+
+/**
+ * Check the sequences API.
+ */
+class DatabaseNextIdCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Sequences API',
+ 'description' => 'Test the secondary sequences API.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that the sequences API work.
+ */
+ function testDbNextId() {
+ $first = db_next_id();
+ $second = db_next_id();
+ // We can test for exact increase in here because we know there is no
+ // other process operating on these tables -- normally we could only
+ // expect $second > $first.
+ $this->assertEqual($first + 1, $second, 'The second call from a sequence provides a number increased by one.');
+ $result = db_next_id(1000);
+ $this->assertEqual($result, 1001, 'Sequence provides a larger number than the existing ID.');
+ }
+}
+
+/**
+ * Tests the empty pseudo-statement class.
+ */
+class DatabaseEmptyStatementTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Empty statement',
+ 'description' => 'Test the empty pseudo-statement class.',
+ 'group' => 'Database',
+ );
+ }
+
+ /**
+ * Test that the empty result set behaves as empty.
+ */
+ function testEmpty() {
+ $result = new DatabaseStatementEmpty();
+
+ $this->assertTrue($result instanceof DatabaseStatementInterface, 'Class implements expected interface');
+ $this->assertNull($result->fetchObject(), 'Null result returned.');
+ }
+
+ /**
+ * Test that the empty result set iterates safely.
+ */
+ function testEmptyIteration() {
+ $result = new DatabaseStatementEmpty();
+
+ foreach ($result as $record) {
+ $this->fail('Iterating empty result set should not iterate.');
+ return;
+ }
+
+ $this->pass('Iterating empty result set skipped iteration.');
+ }
+
+ /**
+ * Test that the empty result set mass-fetches in an expected way.
+ */
+ function testEmptyFetchAll() {
+ $result = new DatabaseStatementEmpty();
+
+ $this->assertEqual($result->fetchAll(), array(), 'Empty array returned from empty result set.');
+ }
+}
+
+/**
+ * Tests management of database connections.
+ */
+class ConnectionUnitTest extends DrupalUnitTestCase {
+
+ protected $key;
+ protected $target;
+
+ protected $monitor;
+ protected $originalCount;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Connection unit tests',
+ 'description' => 'Tests management of database connections.',
+ 'group' => 'Database',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ $this->key = 'default';
+ $this->originalTarget = 'default';
+ $this->target = 'DatabaseConnectionUnitTest';
+
+ // Determine whether the database driver is MySQL. If it is not, the test
+ // methods will not be executed.
+ // @todo Make this test driver-agnostic, or find a proper way to skip it.
+ // @see http://drupal.org/node/1273478
+ $connection_info = Database::getConnectionInfo('default');
+ $this->skipTest = (bool) $connection_info['default']['driver'] != 'mysql';
+ if ($this->skipTest) {
+ // Insert an assertion to prevent Simpletest from interpreting the test
+ // as failure.
+ $this->pass('This test is only compatible with MySQL.');
+ }
+
+ // Create an additional connection to monitor the connections being opened
+ // and closed in this test.
+ // @see TestBase::changeDatabasePrefix()
+ $connection_info = Database::getConnectionInfo('default');
+ Database::addConnectionInfo('default', 'monitor', $connection_info['default']);
+ global $databases;
+ $databases['default']['monitor'] = $connection_info['default'];
+ $this->monitor = Database::getConnection('monitor');
+ }
+
+ /**
+ * Adds a new database connection info to Database.
+ */
+ protected function addConnection() {
+ // Add a new target to the connection, by cloning the current connection.
+ $connection_info = Database::getConnectionInfo($this->key);
+ Database::addConnectionInfo($this->key, $this->target, $connection_info[$this->originalTarget]);
+
+ // Verify that the new target exists.
+ $info = Database::getConnectionInfo($this->key);
+ // Note: Custom assertion message to not expose database credentials.
+ $this->assertIdentical($info[$this->target], $connection_info[$this->key], 'New connection info found.');
+ }
+
+ /**
+ * Returns the connection ID of the current test connection.
+ *
+ * @return integer
+ */
+ protected function getConnectionID() {
+ return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField();
+ }
+
+ /**
+ * Asserts that a connection ID exists.
+ *
+ * @param integer $id
+ * The connection ID to verify.
+ */
+ protected function assertConnection($id) {
+ $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
+ return $this->assertTrue(isset($list[$id]), format_string('Connection ID @id found.', array('@id' => $id)));
+ }
+
+ /**
+ * Asserts that a connection ID does not exist.
+ *
+ * @param integer $id
+ * The connection ID to verify.
+ */
+ protected function assertNoConnection($id) {
+ $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
+ return $this->assertFalse(isset($list[$id]), format_string('Connection ID @id not found.', array('@id' => $id)));
+ }
+
+ /**
+ * Tests Database::closeConnection() without query.
+ *
+ * @todo getConnectionID() executes a query.
+ */
+ function testOpenClose() {
+ if ($this->skipTest) {
+ return;
+ }
+ // Add and open a new connection.
+ $this->addConnection();
+ $id = $this->getConnectionID();
+ Database::getConnection($this->target, $this->key);
+
+ // Verify that there is a new connection.
+ $this->assertConnection($id);
+
+ // Close the connection.
+ Database::closeConnection($this->target, $this->key);
+ // Wait 20ms to give the database engine sufficient time to react.
+ usleep(20000);
+
+ // Verify that we are back to the original connection count.
+ $this->assertNoConnection($id);
+ }
+
+ /**
+ * Tests Database::closeConnection() with a query.
+ */
+ function testOpenQueryClose() {
+ if ($this->skipTest) {
+ return;
+ }
+ // Add and open a new connection.
+ $this->addConnection();
+ $id = $this->getConnectionID();
+ Database::getConnection($this->target, $this->key);
+
+ // Verify that there is a new connection.
+ $this->assertConnection($id);
+
+ // Execute a query.
+ Database::getConnection($this->target, $this->key)->query('SHOW TABLES');
+
+ // Close the connection.
+ Database::closeConnection($this->target, $this->key);
+ // Wait 20ms to give the database engine sufficient time to react.
+ usleep(20000);
+
+ // Verify that we are back to the original connection count.
+ $this->assertNoConnection($id);
+ }
+
+ /**
+ * Tests Database::closeConnection() with a query and custom prefetch method.
+ */
+ function testOpenQueryPrefetchClose() {
+ if ($this->skipTest) {
+ return;
+ }
+ // Add and open a new connection.
+ $this->addConnection();
+ $id = $this->getConnectionID();
+ Database::getConnection($this->target, $this->key);
+
+ // Verify that there is a new connection.
+ $this->assertConnection($id);
+
+ // Execute a query.
+ Database::getConnection($this->target, $this->key)->query('SHOW TABLES')->fetchCol();
+
+ // Close the connection.
+ Database::closeConnection($this->target, $this->key);
+ // Wait 20ms to give the database engine sufficient time to react.
+ usleep(20000);
+
+ // Verify that we are back to the original connection count.
+ $this->assertNoConnection($id);
+ }
+
+ /**
+ * Tests Database::closeConnection() with a select query.
+ */
+ function testOpenSelectQueryClose() {
+ if ($this->skipTest) {
+ return;
+ }
+ // Add and open a new connection.
+ $this->addConnection();
+ $id = $this->getConnectionID();
+ Database::getConnection($this->target, $this->key);
+
+ // Verify that there is a new connection.
+ $this->assertConnection($id);
+
+ // Create a table.
+ $name = 'foo';
+ Database::getConnection($this->target, $this->key)->schema()->createTable($name, array(
+ 'fields' => array(
+ 'name' => array(
+ 'type' => 'varchar',
+ 'length' => 255,
+ ),
+ ),
+ ));
+
+ // Execute a query.
+ Database::getConnection($this->target, $this->key)->select('foo', 'f')
+ ->fields('f', array('name'))
+ ->execute()
+ ->fetchAll();
+
+ // Drop the table.
+ Database::getConnection($this->target, $this->key)->schema()->dropTable($name);
+
+ // Close the connection.
+ Database::closeConnection($this->target, $this->key);
+ // Wait 20ms to give the database engine sufficient time to react.
+ usleep(20000);
+
+ // Verify that we are back to the original connection count.
+ $this->assertNoConnection($id);
+ }
+
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info
new file mode 100644
index 0000000..95acf73
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.info
@@ -0,0 +1,12 @@
+name = "Drupal system listing compatible test"
+description = "Support module for testing the drupal_system_listing function."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_compatible_test/drupal_system_listing_compatible_test.module
@@ -0,0 +1 @@
+<?php
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info
new file mode 100644
index 0000000..60bc34f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.info
@@ -0,0 +1,12 @@
+name = "Drupal system listing incompatible test"
+description = "Support module for testing the drupal_system_listing function."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/drupal_system_listing_incompatible_test/drupal_system_listing_incompatible_test.module
@@ -0,0 +1 @@
+<?php
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test.info
new file mode 100644
index 0000000..d25db8f
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test.info
@@ -0,0 +1,13 @@
+name = "Entity cache test"
+description = "Support module for testing entity cache."
+package = Testing
+version = VERSION
+core = 7.x
+dependencies[] = entity_cache_test_dependency
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test.module
new file mode 100644
index 0000000..5ae9ecc
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test.module
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Helper module for entity cache tests.
+ */
+
+/**
+ * Implements hook_watchdog().
+ *
+ * This hook is called during module_enable() and since this hook
+ * implementation is invoked, we have to expect that this module and dependent
+ * modules have been properly installed already. So we expect to be able to
+ * retrieve the entity information that has been registered by the required
+ * dependency module.
+ *
+ * @see EnableDisableTestCase::testEntityCache()
+ * @see entity_cache_test_dependency_entity_info()
+ */
+function entity_cache_test_watchdog($log_entry) {
+ if ($log_entry['type'] == 'system' && $log_entry['message'] == '%module module installed.') {
+ $info = entity_get_info('entity_cache_test');
+ // Store the information in a system variable to analyze it later in the
+ // test case.
+ variable_set('entity_cache_test', $info);
+ }
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test_dependency.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test_dependency.info
new file mode 100644
index 0000000..41e9d72
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test_dependency.info
@@ -0,0 +1,12 @@
+name = "Entity cache test dependency"
+description = "Support dependency module for testing entity cache."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test_dependency.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test_dependency.module
new file mode 100644
index 0000000..2d4b3be
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_cache_test_dependency.module
@@ -0,0 +1,17 @@
+<?php
+
+/**
+ * @file
+ * Helper module for entity cache tests.
+ */
+
+/**
+ * Implements hook_entity_info().
+ */
+function entity_cache_test_dependency_entity_info() {
+ return array(
+ 'entity_cache_test' => array(
+ 'label' => variable_get('entity_cache_test_label', 'Entity Cache Test'),
+ ),
+ );
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.info
new file mode 100644
index 0000000..00ace08
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.info
@@ -0,0 +1,12 @@
+name = "Entity CRUD Hooks Test"
+description = "Support module for CRUD hook tests."
+core = 7.x
+package = Testing
+version = VERSION
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.module
new file mode 100644
index 0000000..d25dff1
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.module
@@ -0,0 +1,251 @@
+<?php
+
+/**
+ * @file
+ * Test module for the Entity CRUD API.
+ */
+
+/**
+ * Implements hook_entity_presave().
+ */
+function entity_crud_hook_test_entity_presave($entity, $type) {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_presave().
+ */
+function entity_crud_hook_test_comment_presave() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_presave().
+ */
+function entity_crud_hook_test_file_presave() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_presave().
+ */
+function entity_crud_hook_test_node_presave() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_presave().
+ */
+function entity_crud_hook_test_taxonomy_term_presave() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_presave().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_presave() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_presave().
+ */
+function entity_crud_hook_test_user_presave() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function entity_crud_hook_test_entity_insert($entity, $type) {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_insert().
+ */
+function entity_crud_hook_test_comment_insert() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_insert().
+ */
+function entity_crud_hook_test_file_insert() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_insert().
+ */
+function entity_crud_hook_test_node_insert() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_insert().
+ */
+function entity_crud_hook_test_taxonomy_term_insert() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_insert().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_insert() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_insert().
+ */
+function entity_crud_hook_test_user_insert() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_entity_load().
+ */
+function entity_crud_hook_test_entity_load(array $entities, $type) {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_load().
+ */
+function entity_crud_hook_test_comment_load() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_load().
+ */
+function entity_crud_hook_test_file_load() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_load().
+ */
+function entity_crud_hook_test_node_load() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_load().
+ */
+function entity_crud_hook_test_taxonomy_term_load() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_load().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_load() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_load().
+ */
+function entity_crud_hook_test_user_load() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function entity_crud_hook_test_entity_update($entity, $type) {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_update().
+ */
+function entity_crud_hook_test_comment_update() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_update().
+ */
+function entity_crud_hook_test_file_update() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_update().
+ */
+function entity_crud_hook_test_node_update() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_update().
+ */
+function entity_crud_hook_test_taxonomy_term_update() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_update().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_update() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_update().
+ */
+function entity_crud_hook_test_user_update() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function entity_crud_hook_test_entity_delete($entity, $type) {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called for type ' . $type);
+}
+
+/**
+ * Implements hook_comment_delete().
+ */
+function entity_crud_hook_test_comment_delete() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_file_delete().
+ */
+function entity_crud_hook_test_file_delete() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_node_delete().
+ */
+function entity_crud_hook_test_node_delete() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_term_delete().
+ */
+function entity_crud_hook_test_taxonomy_term_delete() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_delete().
+ */
+function entity_crud_hook_test_taxonomy_vocabulary_delete() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
+
+/**
+ * Implements hook_user_delete().
+ */
+function entity_crud_hook_test_user_delete() {
+ $_SESSION['entity_crud_hook_test'][] = (__FUNCTION__ . ' called');
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.test
new file mode 100644
index 0000000..178e34d
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_crud_hook_test.test
@@ -0,0 +1,338 @@
+<?php
+
+/**
+ * @file
+ * CRUD hook tests for the Entity CRUD API.
+ */
+
+/**
+ * Tests invocation of hooks when performing an action.
+ *
+ * Tested hooks are:
+ * - hook_entity_insert()
+ * - hook_entity_load()
+ * - hook_entity_update()
+ * - hook_entity_delete()
+ * As well as all type-specific hooks, like hook_node_insert(),
+ * hook_comment_update(), etc.
+ */
+class EntityCrudHookTestCase extends DrupalWebTestCase {
+
+ protected $ids = array();
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Entity CRUD hooks',
+ 'description' => 'Tests the invocation of hooks when inserting, loading, updating or deleting an entity.',
+ 'group' => 'Entity API',
+ );
+ }
+
+ public function setUp() {
+ parent::setUp('entity_crud_hook_test', 'taxonomy', 'comment');
+ }
+
+ /**
+ * Pass if the message $text was set by one of the CRUD hooks in
+ * entity_crud_hook_test.module, i.e., if the $text is an element of
+ * $_SESSION['entity_crud_hook_test'].
+ *
+ * @param $text
+ * Plain text to look for.
+ * @param $message
+ * Message to display.
+ * @param $group
+ * The group this message belongs to, defaults to 'Other'.
+ * @return
+ * TRUE on pass, FALSE on fail.
+ */
+ protected function assertHookMessage($text, $message = NULL, $group = 'Other') {
+ if (!isset($message)) {
+ $message = $text;
+ }
+ return $this->assertTrue(array_search($text, $_SESSION['entity_crud_hook_test']) !== FALSE, $message, $group);
+ }
+
+ /**
+ * Tests hook invocations for CRUD operations on comments.
+ */
+ public function testCommentHooks() {
+ $node = (object) array(
+ 'uid' => 1,
+ 'type' => 'article',
+ 'title' => 'Test node',
+ 'status' => 1,
+ 'comment' => 2,
+ 'promote' => 0,
+ 'sticky' => 0,
+ 'language' => LANGUAGE_NONE,
+ 'created' => REQUEST_TIME,
+ 'changed' => REQUEST_TIME,
+ );
+ node_save($node);
+ $nid = $node->nid;
+
+ $comment = (object) array(
+ 'cid' => NULL,
+ 'pid' => 0,
+ 'nid' => $nid,
+ 'uid' => 1,
+ 'subject' => 'Test comment',
+ 'created' => REQUEST_TIME,
+ 'changed' => REQUEST_TIME,
+ 'status' => 1,
+ 'language' => LANGUAGE_NONE,
+ );
+ $_SESSION['entity_crud_hook_test'] = array();
+ comment_save($comment);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
+ $this->assertHookMessage('entity_crud_hook_test_comment_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type comment');
+ $this->assertHookMessage('entity_crud_hook_test_comment_insert called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $comment = comment_load($comment->cid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_load called for type comment');
+ $this->assertHookMessage('entity_crud_hook_test_comment_load called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $comment->subject = 'New subject';
+ comment_save($comment);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type comment');
+ $this->assertHookMessage('entity_crud_hook_test_comment_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_update called for type comment');
+ $this->assertHookMessage('entity_crud_hook_test_comment_update called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ comment_delete($comment->cid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type comment');
+ $this->assertHookMessage('entity_crud_hook_test_comment_delete called');
+ }
+
+ /**
+ * Tests hook invocations for CRUD operations on files.
+ */
+ public function testFileHooks() {
+ $url = 'public://entity_crud_hook_test.file';
+ file_put_contents($url, 'Test test test');
+ $file = (object) array(
+ 'fid' => NULL,
+ 'uid' => 1,
+ 'filename' => 'entity_crud_hook_test.file',
+ 'uri' => $url,
+ 'filemime' => 'text/plain',
+ 'filesize' => filesize($url),
+ 'status' => 1,
+ 'timestamp' => REQUEST_TIME,
+ );
+ $_SESSION['entity_crud_hook_test'] = array();
+ file_save($file);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
+ $this->assertHookMessage('entity_crud_hook_test_file_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type file');
+ $this->assertHookMessage('entity_crud_hook_test_file_insert called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $file = file_load($file->fid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_load called for type file');
+ $this->assertHookMessage('entity_crud_hook_test_file_load called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $file->filename = 'new.entity_crud_hook_test.file';
+ file_save($file);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type file');
+ $this->assertHookMessage('entity_crud_hook_test_file_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_update called for type file');
+ $this->assertHookMessage('entity_crud_hook_test_file_update called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ file_delete($file);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type file');
+ $this->assertHookMessage('entity_crud_hook_test_file_delete called');
+ }
+
+ /**
+ * Tests hook invocations for CRUD operations on nodes.
+ */
+ public function testNodeHooks() {
+ $node = (object) array(
+ 'uid' => 1,
+ 'type' => 'article',
+ 'title' => 'Test node',
+ 'status' => 1,
+ 'comment' => 2,
+ 'promote' => 0,
+ 'sticky' => 0,
+ 'language' => LANGUAGE_NONE,
+ 'created' => REQUEST_TIME,
+ 'changed' => REQUEST_TIME,
+ );
+ $_SESSION['entity_crud_hook_test'] = array();
+ node_save($node);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
+ $this->assertHookMessage('entity_crud_hook_test_node_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type node');
+ $this->assertHookMessage('entity_crud_hook_test_node_insert called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $node = node_load($node->nid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_load called for type node');
+ $this->assertHookMessage('entity_crud_hook_test_node_load called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $node->title = 'New title';
+ node_save($node);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type node');
+ $this->assertHookMessage('entity_crud_hook_test_node_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_update called for type node');
+ $this->assertHookMessage('entity_crud_hook_test_node_update called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ node_delete($node->nid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type node');
+ $this->assertHookMessage('entity_crud_hook_test_node_delete called');
+ }
+
+ /**
+ * Tests hook invocations for CRUD operations on taxonomy terms.
+ */
+ public function testTaxonomyTermHooks() {
+ $vocabulary = (object) array(
+ 'name' => 'Test vocabulary',
+ 'machine_name' => 'test',
+ 'description' => NULL,
+ 'module' => 'entity_crud_hook_test',
+ );
+ taxonomy_vocabulary_save($vocabulary);
+
+ $term = (object) array(
+ 'vid' => $vocabulary->vid,
+ 'name' => 'Test term',
+ 'description' => NULL,
+ 'format' => 1,
+ );
+ $_SESSION['entity_crud_hook_test'] = array();
+ taxonomy_term_save($term);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_term');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_insert called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $term = taxonomy_term_load($term->tid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_term');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_load called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $term->name = 'New name';
+ taxonomy_term_save($term);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_term');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_term');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_update called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ taxonomy_term_delete($term->tid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_term');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_term_delete called');
+ }
+
+ /**
+ * Tests hook invocations for CRUD operations on taxonomy vocabularies.
+ */
+ public function testTaxonomyVocabularyHooks() {
+ $vocabulary = (object) array(
+ 'name' => 'Test vocabulary',
+ 'machine_name' => 'test',
+ 'description' => NULL,
+ 'module' => 'entity_crud_hook_test',
+ );
+ $_SESSION['entity_crud_hook_test'] = array();
+ taxonomy_vocabulary_save($vocabulary);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_insert called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $vocabulary = taxonomy_vocabulary_load($vocabulary->vid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_load called for type taxonomy_vocabulary');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_load called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $vocabulary->name = 'New name';
+ taxonomy_vocabulary_save($vocabulary);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_update called for type taxonomy_vocabulary');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_update called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ taxonomy_vocabulary_delete($vocabulary->vid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary');
+ $this->assertHookMessage('entity_crud_hook_test_taxonomy_vocabulary_delete called');
+ }
+
+ /**
+ * Tests hook invocations for CRUD operations on users.
+ */
+ public function testUserHooks() {
+ $edit = array(
+ 'name' => 'Test user',
+ 'mail' => 'test@example.com',
+ 'created' => REQUEST_TIME,
+ 'status' => 1,
+ 'language' => 'en',
+ );
+ $account = (object) $edit;
+ $_SESSION['entity_crud_hook_test'] = array();
+ $account = user_save($account, $edit);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
+ $this->assertHookMessage('entity_crud_hook_test_user_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_insert called for type user');
+ $this->assertHookMessage('entity_crud_hook_test_user_insert called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $account = user_load($account->uid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_load called for type user');
+ $this->assertHookMessage('entity_crud_hook_test_user_load called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ $edit['name'] = 'New name';
+ $account = user_save($account, $edit);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_presave called for type user');
+ $this->assertHookMessage('entity_crud_hook_test_user_presave called');
+ $this->assertHookMessage('entity_crud_hook_test_entity_update called for type user');
+ $this->assertHookMessage('entity_crud_hook_test_user_update called');
+
+ $_SESSION['entity_crud_hook_test'] = array();
+ user_delete($account->uid);
+
+ $this->assertHookMessage('entity_crud_hook_test_entity_delete called for type user');
+ $this->assertHookMessage('entity_crud_hook_test_user_delete called');
+ }
+
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query.test
new file mode 100644
index 0000000..9a6cb69
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query.test
@@ -0,0 +1,1681 @@
+<?php
+
+
+/**
+ * @file
+ * Unit test file for the entity API.
+ */
+
+/**
+ * Tests EntityFieldQuery.
+ */
+class EntityFieldQueryTestCase extends DrupalWebTestCase {
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'Entity query',
+ 'description' => 'Test the EntityFieldQuery class.',
+ 'group' => 'Entity API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp(array('node', 'field_test', 'entity_query_access_test', 'node_access_test'));
+
+ field_test_create_bundle('bundle1');
+ field_test_create_bundle('bundle2');
+ field_test_create_bundle('test_bundle');
+ field_test_create_bundle('test_entity_bundle');
+
+ $instances = array();
+ $this->fields = array();
+ $this->field_names[0] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
+ $field = array('field_name' => $field_name, 'type' => 'test_field', 'cardinality' => 4);
+ $field = field_create_field($field);
+ $this->fields[0] = $field;
+ $instance = array(
+ 'field_name' => $field_name,
+ 'entity_type' => '',
+ 'bundle' => '',
+ 'label' => $this->randomName() . '_label',
+ 'description' => $this->randomName() . '_description',
+ 'weight' => mt_rand(0, 127),
+ 'settings' => array(
+ 'test_instance_setting' => $this->randomName(),
+ ),
+ 'widget' => array(
+ 'type' => 'test_field_widget',
+ 'label' => 'Test Field',
+ 'settings' => array(
+ 'test_widget_setting' => $this->randomName(),
+ )
+ )
+ );
+
+ $instances[0] = $instance;
+
+ // Add an instance to that bundle.
+ $instances[0]['bundle'] = 'bundle1';
+ $instances[0]['entity_type'] = 'test_entity_bundle_key';
+ field_create_instance($instances[0]);
+ $instances[0]['bundle'] = 'bundle2';
+ field_create_instance($instances[0]);
+ $instances[0]['bundle'] = $instances[0]['entity_type'] = 'test_entity_bundle';
+ field_create_instance($instances[0]);
+
+ $this->field_names[1] = $field_name = drupal_strtolower($this->randomName() . '_field_name');
+ $field = array('field_name' => $field_name, 'type' => 'shape', 'cardinality' => 4);
+ $field = field_create_field($field);
+ $this->fields[1] = $field;
+ $instance = array(
+ 'field_name' => $field_name,
+ 'entity_type' => '',
+ 'bundle' => '',
+ 'label' => $this->randomName() . '_label',
+ 'description' => $this->randomName() . '_description',
+ 'weight' => mt_rand(0, 127),
+ 'settings' => array(
+ 'test_instance_setting' => $this->randomName(),
+ ),
+ 'widget' => array(
+ 'type' => 'test_field_widget',
+ 'label' => 'Test Field',
+ 'settings' => array(
+ 'test_widget_setting' => $this->randomName(),
+ )
+ )
+ );
+
+ $instances[1] = $instance;
+
+ // Add a field instance to the bundles.
+ $instances[1]['bundle'] = 'bundle1';
+ $instances[1]['entity_type'] = 'test_entity_bundle_key';
+ field_create_instance($instances[1]);
+ $instances[1]['bundle'] = $instances[1]['entity_type'] = 'test_entity_bundle';
+ field_create_instance($instances[1]);
+
+ $this->instances = $instances;
+ // Write entity base table if there is one.
+ $entities = array();
+
+ // Create entities which have a 'bundle key' defined.
+ for ($i = 1; $i < 7; $i++) {
+ $entity = new stdClass();
+ $entity->ftid = $i;
+ $entity->fttype = ($i < 5) ? 'bundle1' : 'bundle2';
+
+ $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+ drupal_write_record('test_entity_bundle_key', $entity);
+ field_attach_insert('test_entity_bundle_key', $entity);
+ }
+
+ $entity = new stdClass();
+ $entity->ftid = 5;
+ $entity->fttype = 'test_entity_bundle';
+ $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['shape'] = 'square';
+ $entity->{$this->field_names[1]}[LANGUAGE_NONE][0]['color'] = 'red';
+ $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['shape'] = 'circle';
+ $entity->{$this->field_names[1]}[LANGUAGE_NONE][1]['color'] = 'blue';
+ drupal_write_record('test_entity_bundle', $entity);
+ field_attach_insert('test_entity_bundle', $entity);
+
+ $instances[2] = $instance;
+ $instances[2]['bundle'] = 'test_bundle';
+ $instances[2]['field_name'] = $this->field_names[0];
+ $instances[2]['entity_type'] = 'test_entity';
+ field_create_instance($instances[2]);
+
+ // Create entities with support for revisions.
+ for ($i = 1; $i < 5; $i++) {
+ $entity = new stdClass();
+ $entity->ftid = $i;
+ $entity->ftvid = $i;
+ $entity->fttype = 'test_bundle';
+ $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+
+ drupal_write_record('test_entity', $entity);
+ field_attach_insert('test_entity', $entity);
+ drupal_write_record('test_entity_revision', $entity);
+ }
+
+ // Add two revisions to an entity.
+ for ($i = 100; $i < 102; $i++) {
+ $entity = new stdClass();
+ $entity->ftid = 4;
+ $entity->ftvid = $i;
+ $entity->fttype = 'test_bundle';
+ $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i;
+
+ drupal_write_record('test_entity', $entity, 'ftid');
+ drupal_write_record('test_entity_revision', $entity);
+
+ db_update('test_entity')
+ ->fields(array('ftvid' => $entity->ftvid))
+ ->condition('ftid', $entity->ftid)
+ ->execute();
+
+ field_attach_update('test_entity', $entity);
+ }
+ }
+
+ /**
+ * Tests EntityFieldQuery.
+ */
+ function testEntityFieldQuery() {
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle')
+ ->entityCondition('entity_id', '5');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle', 5),
+ ), 'Test query on an entity type with a generated bundle.');
+
+ // Test entity_type condition.
+ $query = new EntityFieldQuery();
+ $query->entityCondition('entity_type', 'test_entity_bundle_key');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test entity entity_type condition.');
+
+ // Test entity_id condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityCondition('entity_id', '3');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ ), 'Test entity entity_id condition.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', '3');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ ), 'Test entity entity_id condition and entity_id property condition.');
+
+ // Test bundle condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityCondition('bundle', 'bundle1');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test entity bundle condition: bundle1.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityCondition('bundle', 'bundle2');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test entity bundle condition: bundle2.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('fttype', 'bundle2');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test entity bundle condition and bundle property condition.');
+
+ // Test revision_id condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->entityCondition('revision_id', '3');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 3),
+ ), 'Test entity revision_id condition.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->propertyCondition('ftvid', '3');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 3),
+ ), 'Test entity revision_id condition and revision_id property condition.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->fieldCondition($this->fields[0], 'value', 100, '>=')
+ ->age(FIELD_LOAD_REVISION);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 100),
+ array('test_entity', 101),
+ ), 'Test revision age.');
+
+ // Test that fields attached to the non-revision supporting entity
+ // 'test_entity_bundle_key' are reachable in FIELD_LOAD_REVISION.
+ $query = new EntityFieldQuery();
+ $query
+ ->fieldCondition($this->fields[0], 'value', 100, '<')
+ ->age(FIELD_LOAD_REVISION);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity', 1),
+ array('test_entity', 2),
+ array('test_entity', 3),
+ array('test_entity', 4),
+ ), 'Test that fields are reachable from FIELD_LOAD_REVISION even for non-revision entities.');
+
+ // Test entity sort by entity_id.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityOrderBy('entity_id', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test sort entity entity_id in ascending order.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityOrderBy('entity_id', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test sort entity entity_id in descending order.', TRUE);
+
+ // Test entity sort by entity_id, with a field condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->entityOrderBy('entity_id', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test sort entity entity_id in ascending order, with a field condition.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->propertyOrderBy('ftid', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test sort entity entity_id property in descending order, with a field condition.', TRUE);
+
+ // Test property sort by entity id.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyOrderBy('ftid', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test sort entity entity_id property in ascending order.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyOrderBy('ftid', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test sort entity entity_id property in descending order.', TRUE);
+
+ // Test property sort by entity id, with a field condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->propertyOrderBy('ftid', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test sort entity entity_id property in ascending order, with a field condition.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->propertyOrderBy('ftid', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test sort entity entity_id property in descending order, with a field condition.', TRUE);
+
+ // Test entity sort by bundle.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityOrderBy('bundle', 'ASC')
+ ->propertyOrderBy('ftid', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ ), 'Test sort entity bundle in ascending order, property in descending order.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityOrderBy('bundle', 'DESC')
+ ->propertyOrderBy('ftid', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test sort entity bundle in descending order, property in ascending order.', TRUE);
+
+ // Test entity sort by bundle, with a field condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->entityOrderBy('bundle', 'ASC')
+ ->propertyOrderBy('ftid', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ ), 'Test sort entity bundle in ascending order, property in descending order, with a field condition.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->entityOrderBy('bundle', 'DESC')
+ ->propertyOrderBy('ftid', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test sort entity bundle in descending order, property in ascending order, with a field condition.', TRUE);
+
+ // Test entity sort by bundle, field.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityOrderBy('bundle', 'ASC')
+ ->fieldOrderBy($this->fields[0], 'value', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ ), 'Test sort entity bundle in ascending order, field in descending order.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityOrderBy('bundle', 'DESC')
+ ->fieldOrderBy($this->fields[0], 'value', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test sort entity bundle in descending order, field in ascending order.', TRUE);
+
+ // Test entity sort by revision_id.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->entityOrderBy('revision_id', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ array('test_entity', 2),
+ array('test_entity', 3),
+ array('test_entity', 4),
+ ), 'Test sort entity revision_id in ascending order.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->entityOrderBy('revision_id', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 4),
+ array('test_entity', 3),
+ array('test_entity', 2),
+ array('test_entity', 1),
+ ), 'Test sort entity revision_id in descending order.', TRUE);
+
+ // Test entity sort by revision_id, with a field condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->entityOrderBy('revision_id', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ array('test_entity', 2),
+ array('test_entity', 3),
+ array('test_entity', 4),
+ ), 'Test sort entity revision_id in ascending order, with a field condition.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->entityOrderBy('revision_id', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 4),
+ array('test_entity', 3),
+ array('test_entity', 2),
+ array('test_entity', 1),
+ ), 'Test sort entity revision_id in descending order, with a field condition.', TRUE);
+
+ // Test property sort by revision_id.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->propertyOrderBy('ftvid', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ array('test_entity', 2),
+ array('test_entity', 3),
+ array('test_entity', 4),
+ ), 'Test sort entity revision_id property in ascending order.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->propertyOrderBy('ftvid', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 4),
+ array('test_entity', 3),
+ array('test_entity', 2),
+ array('test_entity', 1),
+ ), 'Test sort entity revision_id property in descending order.', TRUE);
+
+ // Test property sort by revision_id, with a field condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->propertyOrderBy('ftvid', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ array('test_entity', 2),
+ array('test_entity', 3),
+ array('test_entity', 4),
+ ), 'Test sort entity revision_id property in ascending order, with a field condition.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->propertyOrderBy('ftvid', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 4),
+ array('test_entity', 3),
+ array('test_entity', 2),
+ array('test_entity', 1),
+ ), 'Test sort entity revision_id property in descending order, with a field condition.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldOrderBy($this->fields[0], 'value', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test sort field in ascending order without field condition.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldOrderBy($this->fields[0], 'value', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test sort field in descending order without field condition.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->fieldOrderBy($this->fields[0], 'value', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test sort field in ascending order.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->fieldOrderBy($this->fields[0], 'value', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test sort field in descending order.', TRUE);
+
+ // Test "in" operation with entity entity_type condition and entity_id
+ // property condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', array(1, 3, 4), 'IN');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test "in" operation with entity entity_type condition and entity_id property condition.');
+
+ // Test "in" operation with entity entity_type condition and entity_id
+ // property condition. Sort in descending order by entity_id.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', array(1, 3, 4), 'IN')
+ ->propertyOrderBy('ftid', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 1),
+ ), 'Test "in" operation with entity entity_type condition and entity_id property condition. Sort entity_id in descending order.', TRUE);
+
+ // Test query count
+ $query = new EntityFieldQuery();
+ $query_count = $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->count()
+ ->execute();
+ $this->assertEqual($query_count, 6, 'Test query count on entity condition.');
+
+ $query = new EntityFieldQuery();
+ $query_count = $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', '1')
+ ->count()
+ ->execute();
+ $this->assertEqual($query_count, 1, 'Test query count on entity and property condition.');
+
+ $query = new EntityFieldQuery();
+ $query_count = $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', '4', '>')
+ ->count()
+ ->execute();
+ $this->assertEqual($query_count, 2, 'Test query count on entity and property condition with operator.');
+
+ $query = new EntityFieldQuery();
+ $query_count = $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 3, '=')
+ ->count()
+ ->execute();
+ $this->assertEqual($query_count, 1, 'Test query count on field condition.');
+
+ // First, test without options.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('fttype', 'und', 'CONTAINS');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test the "contains" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[1], 'shape', 'uar', 'CONTAINS');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle', 5),
+ ), 'Test the "contains" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', 1, '=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ ), 'Test the "equal to" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 3, '=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity', 3),
+ ), 'Test the "equal to" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', 3, '<>');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test the "not equal to" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 3, '<>');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity', 1),
+ array('test_entity', 2),
+ array('test_entity', 4),
+ ), 'Test the "not equal to" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', 3, '!=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test the "not equal to" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 3, '!=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity', 1),
+ array('test_entity', 2),
+ array('test_entity', 4),
+ ), 'Test the "not equal to" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', 2, '<');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ ), 'Test the "less than" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 2, '<');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity', 1),
+ ), 'Test the "less than" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', 2, '<=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ ), 'Test the "less than or equal to" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 2, '<=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity', 1),
+ array('test_entity', 2),
+ ), 'Test the "less than or equal to" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', 4, '>');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test the "greater than" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 2, '>');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity', 3),
+ array('test_entity', 4),
+ ), 'Test the "greater than" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', 4, '>=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test the "greater than or equal to" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 3, '>=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity', 3),
+ array('test_entity', 4),
+ ), 'Test the "greater than or equal to" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', array(3, 4), 'NOT IN');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test the "not in" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', array(3, 4, 100, 101), 'NOT IN');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity', 1),
+ array('test_entity', 2),
+ ), 'Test the "not in" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', array(3, 4), 'IN');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test the "in" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', array(2, 3), 'IN');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity', 2),
+ array('test_entity', 3),
+ ), 'Test the "in" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', array(1, 3), 'BETWEEN');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ ), 'Test the "between" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', array(1, 3), 'BETWEEN');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity', 1),
+ array('test_entity', 2),
+ array('test_entity', 3),
+ ), 'Test the "between" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('fttype', 'bun', 'STARTS_WITH');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test the "starts_with" operation on a property.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle', 5),
+ ), 'Test the "starts_with" operation on a field.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 3);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity', 3),
+ ), 'Test omission of an operator with a single item.');
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', array(2, 3));
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity', 2),
+ array('test_entity', 3),
+ ), 'Test omission of an operator with multiple items.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyCondition('ftid', 1, '>')
+ ->fieldCondition($this->fields[0], 'value', 4, '<');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ ), 'Test entity, property and field conditions.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->entityCondition('bundle', 'bundle', 'STARTS_WITH')
+ ->propertyCondition('ftid', 4)
+ ->fieldCondition($this->fields[0], 'value', 4);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 4),
+ ), 'Test entity condition with "starts_with" operation, and property and field conditions.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyOrderBy('ftid', 'ASC')
+ ->range(0, 2);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ ), 'Test limit on a property.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>=')
+ ->fieldOrderBy($this->fields[0], 'value', 'ASC')
+ ->range(0, 2);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ ), 'Test limit on a field.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyOrderBy('ftid', 'ASC')
+ ->range(4, 6);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test offset on a property.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->fieldOrderBy($this->fields[0], 'value', 'ASC')
+ ->range(2, 4);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test offset on a field.', TRUE);
+
+ for ($i = 6; $i < 10; $i++) {
+ $entity = new stdClass();
+ $entity->ftid = $i;
+ $entity->fttype = 'test_entity_bundle';
+ $entity->{$this->field_names[0]}[LANGUAGE_NONE][0]['value'] = $i - 5;
+ drupal_write_record('test_entity_bundle', $entity);
+ field_attach_insert('test_entity_bundle', $entity);
+ }
+
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', 2, '>');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity', 3),
+ array('test_entity', 4),
+ array('test_entity_bundle', 8),
+ array('test_entity_bundle', 9),
+ ), 'Select a field across multiple entities.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->fieldCondition($this->fields[1], 'shape', 'square')
+ ->fieldCondition($this->fields[1], 'color', 'blue');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle', 5),
+ ), 'Test without a delta group.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->fieldCondition($this->fields[1], 'shape', 'square', '=', 'group')
+ ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'group');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a delta group.');
+
+ // Test query on a deleted field.
+ field_attach_delete_bundle('test_entity_bundle_key', 'bundle1');
+ field_attach_delete_bundle('test_entity', 'test_bundle');
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', '3');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle', 8),
+ ), 'Test query on a field after deleting field from some entities.');
+
+ field_attach_delete_bundle('test_entity_bundle', 'test_entity_bundle');
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', '3');
+ $this->assertEntityFieldQuery($query, array(), 'Test query on a field after deleting field from all entities.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->fieldCondition($this->fields[0], 'value', '3')
+ ->deleted(TRUE);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle', 8),
+ array('test_entity', 3),
+ ), 'Test query on a deleted field with deleted option set to TRUE.');
+
+ $pass = FALSE;
+ $query = new EntityFieldQuery();
+ try {
+ $query->execute();
+ }
+ catch (EntityFieldQueryException $exception) {
+ $pass = ($exception->getMessage() == t('For this query an entity type must be specified.'));
+ }
+ $this->assertTrue($pass, "Can't query the universe.");
+ }
+
+ /**
+ * Tests querying translatable fields.
+ */
+ function testEntityFieldQueryTranslatable() {
+
+ // Make a test field translatable AND cardinality one.
+ $this->fields[0]['translatable'] = TRUE;
+ $this->fields[0]['cardinality'] = 1;
+ field_update_field($this->fields[0]);
+ field_test_entity_info_translatable('test_entity', TRUE);
+
+ // Create more items with different languages.
+ $entity = new stdClass();
+ $entity->ftid = 1;
+ $entity->ftvid = 1;
+ $entity->fttype = 'test_bundle';
+
+ // Set fields in two languages with one field value.
+ foreach (array(LANGUAGE_NONE, 'en') as $langcode) {
+ $entity->{$this->field_names[0]}[$langcode][0]['value'] = 1234;
+ }
+
+ field_attach_update('test_entity', $entity);
+
+ // Look up number of results when querying a single entity with multilingual
+ // field values.
+ $query = new EntityFieldQuery();
+ $query_count = $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->entityCondition('bundle', 'test_bundle')
+ ->entityCondition('entity_id', '1')
+ ->fieldCondition($this->fields[0])
+ ->count()
+ ->execute();
+
+ $this->assertEqual($query_count, 1, "Count on translatable cardinality one field is correct.");
+ }
+
+ /**
+ * Tests field meta conditions.
+ */
+ function testEntityFieldQueryMetaConditions() {
+ // Make a test field translatable.
+ $this->fields[0]['translatable'] = TRUE;
+ field_update_field($this->fields[0]);
+ field_test_entity_info_translatable('test_entity', TRUE);
+
+ // Create more items with different languages.
+ $entity = new stdClass();
+ $entity->ftid = 1;
+ $entity->ftvid = 1;
+ $entity->fttype = 'test_bundle';
+ $j = 0;
+
+ foreach (array(LANGUAGE_NONE, 'en') as $langcode) {
+ for ($i = 0; $i < 4; $i++) {
+ $entity->{$this->field_names[0]}[$langcode][$i]['value'] = $i + $j;
+ }
+ $j += 4;
+ }
+
+ field_attach_update('test_entity', $entity);
+
+ // Test delta field meta condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldDeltaCondition($this->fields[0], 0, '>');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ ), 'Test with a delta meta condition.');
+
+ // Test language field meta condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ ), 'Test with a language meta condition.');
+
+ // Test language field meta condition.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '!=');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ ), 'Test with a language meta condition.');
+
+ // Test delta grouping.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'group')
+ ->fieldDeltaCondition($this->fields[0], 1, '<', 'group');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ ), 'Test with a grouped delta meta condition.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'group')
+ ->fieldDeltaCondition($this->fields[0], 1, '>=', 'group');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta meta condition (empty result set).');
+
+ // Test language grouping.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group')
+ ->fieldLanguageCondition($this->fields[0], 'en', '<>', NULL, 'group');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ ), 'Test with a grouped language meta condition.');
+
+ // Test language grouping.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group')
+ ->fieldLanguageCondition($this->fields[0], 'en', '!=', NULL, 'group');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ ), 'Test with a grouped language meta condition.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group')
+ ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', NULL, 'group');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped language meta condition (empty result set).');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', NULL, 'group')
+ ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '!=', NULL, 'group');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped language meta condition (empty result set).');
+
+ // Test delta and language grouping.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[0], 'en', '<>', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ ), 'Test with a grouped delta + language meta condition.');
+
+ // Test delta and language grouping.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[0], 'en', '!=', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity', 1),
+ ), 'Test with a grouped delta + language meta condition.');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[0], 'en', '<>', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, delta condition unsatisifed).');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[0], 'en', '!=', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, delta condition unsatisifed).');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, language condition unsatisifed).');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[0], 1, '<', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '!=', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, language condition unsatisifed).');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '<>', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, both conditions unsatisifed).');
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity', '=')
+ ->fieldCondition($this->fields[0], 'value', 0, '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[0], 1, '>=', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[0], LANGUAGE_NONE, '!=', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(), 'Test with a grouped delta + language meta condition (empty result set, both conditions unsatisifed).');
+
+ // Test grouping with another field to ensure that grouping cache is reset
+ // properly.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle', '=')
+ ->fieldCondition($this->fields[1], 'shape', 'circle', '=', 'delta', 'language')
+ ->fieldCondition($this->fields[1], 'color', 'blue', '=', 'delta', 'language')
+ ->fieldDeltaCondition($this->fields[1], 1, '=', 'delta', 'language')
+ ->fieldLanguageCondition($this->fields[1], LANGUAGE_NONE, '=', 'delta', 'language');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle', 5),
+ ), 'Test grouping cache.');
+ }
+
+ /**
+ * Tests the routing feature of EntityFieldQuery.
+ */
+ function testEntityFieldQueryRouting() {
+ // Entity-only query.
+ $query = new EntityFieldQuery();
+ $query->entityCondition('entity_type', 'test_entity_bundle_key');
+ $this->assertIdentical($query->queryCallback(), array($query, 'propertyQuery'), 'Entity-only queries are handled by the propertyQuery handler.');
+
+ // Field-only query.
+ $query = new EntityFieldQuery();
+ $query->fieldCondition($this->fields[0], 'value', '3');
+ $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', 'Pure field queries are handled by the Field storage handler.');
+
+ // Mixed entity and field query.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', '3');
+ $this->assertIdentical($query->queryCallback(), 'field_sql_storage_field_storage_query', 'Mixed queries are handled by the Field storage handler.');
+
+ // Overriding with $query->executeCallback.
+ $query = new EntityFieldQuery();
+ $query->entityCondition('entity_type', 'test_entity_bundle_key');
+ $query->executeCallback = 'field_test_dummy_field_storage_query';
+ $this->assertEntityFieldQuery($query, array(
+ array('user', 1),
+ ), 'executeCallback can override the query handler.');
+
+ // Overriding with $query->executeCallback via hook_entity_query_alter().
+ $query = new EntityFieldQuery();
+ $query->entityCondition('entity_type', 'test_entity_bundle_key');
+ // Add a flag that will be caught by field_test_entity_query_alter().
+ $query->alterMyExecuteCallbackPlease = TRUE;
+ $this->assertEntityFieldQuery($query, array(
+ array('user', 1),
+ ), 'executeCallback can override the query handler when set in a hook_entity_query_alter().');
+
+ // Mixed-storage queries.
+ $query = new EntityFieldQuery();
+ $query
+ ->fieldCondition($this->fields[0], 'value', '3')
+ ->fieldCondition($this->fields[1], 'shape', 'squ', 'STARTS_WITH');
+ // Alter the storage of the field.
+ $query->fields[1]['storage']['module'] = 'dummy_storage';
+ try {
+ $query->queryCallback();
+ }
+ catch (EntityFieldQueryException $exception) {
+ $pass = ($exception->getMessage() == t("Can't handle more than one field storage engine"));
+ }
+ $this->assertTrue($pass, 'Cannot query across field storage engines.');
+ }
+
+ /**
+ * Tests the pager integration of EntityFieldQuery.
+ */
+ function testEntityFieldQueryPager() {
+ // Test pager in propertyQuery
+ $_GET['page'] = '0,1';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyOrderBy('ftid', 'ASC')
+ ->pager(3, 0);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ ), 'Test pager integration in propertyQuery: page 1.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyOrderBy('ftid', 'ASC')
+ ->pager(3, 1);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test pager integration in propertyQuery: page 2.', TRUE);
+
+ // Test pager in field storage
+ $_GET['page'] = '0,1';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->propertyOrderBy('ftid', 'ASC')
+ ->pager(2, 0);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ ), 'Test pager integration in field storage: page 1.', TRUE);
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->propertyOrderBy('ftid', 'ASC')
+ ->pager(2, 1);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test pager integration in field storage: page 2.', TRUE);
+
+ unset($_GET['page']);
+ }
+
+ /**
+ * Tests disabling the pager in EntityFieldQuery.
+ */
+ function testEntityFieldQueryDisablePager() {
+ // Test enabling a pager and then disabling it.
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->propertyOrderBy('ftid', 'ASC')
+ ->pager(1)
+ ->pager(0);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'All test entities are listed when the pager is enabled and then disabled.', TRUE);
+ }
+
+ /**
+ * Tests the TableSort integration of EntityFieldQuery.
+ */
+ function testEntityFieldQueryTableSort() {
+ // Test TableSort in propertyQuery
+ $_GET['sort'] = 'asc';
+ $_GET['order'] = 'Id';
+ $header = array(
+ 'id' => array('data' => 'Id', 'type' => 'property', 'specifier' => 'ftid'),
+ 'type' => array('data' => 'Type', 'type' => 'entity', 'specifier' => 'bundle'),
+ );
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->tableSort($header);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test TableSort by property: ftid ASC in propertyQuery.', TRUE);
+
+ $_GET['sort'] = 'desc';
+ $_GET['order'] = 'Id';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->tableSort($header);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test TableSort by property: ftid DESC in propertyQuery.', TRUE);
+
+ $_GET['sort'] = 'asc';
+ $_GET['order'] = 'Type';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->tableSort($header);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test TableSort by entity: bundle ASC in propertyQuery.', TRUE);
+
+ $_GET['sort'] = 'desc';
+ $_GET['order'] = 'Type';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->tableSort($header);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test TableSort by entity: bundle DESC in propertyQuery.', TRUE);
+
+ // Test TableSort in field storage
+ $_GET['sort'] = 'asc';
+ $_GET['order'] = 'Id';
+ $header = array(
+ 'id' => array('data' => 'Id', 'type' => 'property', 'specifier' => 'ftid'),
+ 'type' => array('data' => 'Type', 'type' => 'entity', 'specifier' => 'bundle'),
+ 'field' => array('data' => 'Field', 'type' => 'field', 'specifier' => array('field' => $this->field_names[0], 'column' => 'value')),
+ );
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->tableSort($header);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test TableSort by property: ftid ASC in field storage.', TRUE);
+
+ $_GET['sort'] = 'desc';
+ $_GET['order'] = 'Id';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->tableSort($header);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test TableSort by property: ftid DESC in field storage.', TRUE);
+
+ $_GET['sort'] = 'asc';
+ $_GET['order'] = 'Type';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->tableSort($header)
+ ->entityOrderBy('entity_id', 'DESC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ ), 'Test TableSort by entity: bundle ASC in field storage.', TRUE);
+
+ $_GET['sort'] = 'desc';
+ $_GET['order'] = 'Type';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->tableSort($header)
+ ->entityOrderBy('entity_id', 'ASC');
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ ), 'Test TableSort by entity: bundle DESC in field storage.', TRUE);
+
+ $_GET['sort'] = 'asc';
+ $_GET['order'] = 'Field';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->tableSort($header);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 1),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 6),
+ ), 'Test TableSort by field ASC.', TRUE);
+
+ $_GET['sort'] = 'desc';
+ $_GET['order'] = 'Field';
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($this->fields[0], 'value', 0, '>')
+ ->tableSort($header);
+ $this->assertEntityFieldQuery($query, array(
+ array('test_entity_bundle_key', 6),
+ array('test_entity_bundle_key', 5),
+ array('test_entity_bundle_key', 4),
+ array('test_entity_bundle_key', 3),
+ array('test_entity_bundle_key', 2),
+ array('test_entity_bundle_key', 1),
+ ), 'Test TableSort by field DESC.', TRUE);
+
+ unset($_GET['sort']);
+ unset($_GET['order']);
+ }
+
+ /**
+ * Tests EntityFieldQuery access on non-node entities.
+ */
+ function testEntityFieldQueryAccess() {
+ // Test as a user with ability to bypass node access.
+ $privileged_user = $this->drupalCreateUser(array('bypass node access', 'access content'));
+ $this->drupalLogin($privileged_user);
+ $this->drupalGet('entity-query-access/test/' . $this->fields[0]['field_name']);
+ $this->assertText('Found entity', 'Returned access response with entities.');
+ $this->drupalLogout();
+
+ // Test as a user that does not have ability to bypass node access or view
+ // all nodes.
+ $regular_user = $this->drupalCreateUser(array('access content'));
+ $this->drupalLogin($regular_user);
+ $this->drupalGet('entity-query-access/test/' . $this->fields[0]['field_name']);
+ $this->assertText('Found entity', 'Returned access response with entities.');
+ $this->drupalLogout();
+ }
+
+ /**
+ * Fetches the results of an EntityFieldQuery and compares.
+ *
+ * @param $query
+ * An EntityFieldQuery to run.
+ * @param $intended_results
+ * A list of results, every entry is again a list, first being the entity
+ * type, the second being the entity_id.
+ * @param $message
+ * The message to be displayed as the result of this test.
+ * @param $ordered
+ * If FALSE then the result of EntityFieldQuery will match
+ * $intended_results even if the order is not the same. If TRUE then order
+ * should match too.
+ */
+ function assertEntityFieldQuery($query, $intended_results, $message, $ordered = FALSE) {
+ $results = array();
+ try {
+ foreach ($query->execute() as $entity_type => $entity_ids) {
+ foreach ($entity_ids as $entity_id => $stub_entity) {
+ $results[] = array($entity_type, $entity_id);
+ }
+ }
+ if (!isset($ordered) || !$ordered) {
+ sort($results);
+ sort($intended_results);
+ }
+ $this->assertEqual($results, $intended_results, $message);
+ }
+ catch (Exception $e) {
+ $this->fail('Exception thrown: '. $e->getMessage());
+ }
+ }
+
+ /**
+ * Tests EFQ table prefixing with multiple conditions and an altered join.
+ *
+ * @see field_test_query_efq_table_prefixing_test_alter()
+ */
+ function testTablePrefixing() {
+ $query = new EntityFieldQuery();
+ $query = $query
+ ->entityCondition('entity_type', 'test_entity')
+ ->entityCondition('bundle', 'test_bundle')
+ ->entityCondition('entity_id', '1')
+ ->addTag('efq_table_prefixing_test');
+
+ $expected = array(array('test_entity', 1));
+
+ $this->assertEntityFieldQuery($query, $expected, 'An EntityFieldQuery returns the expected results when altered with an additional join on the base table.');
+ }
+
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query_access_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query_access_test.info
new file mode 100644
index 0000000..4abb440
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query_access_test.info
@@ -0,0 +1,12 @@
+name = "Entity query access test"
+description = "Support module for checking entity query results."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query_access_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query_access_test.module
new file mode 100644
index 0000000..53641af
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/entity_query_access_test.module
@@ -0,0 +1,54 @@
+<?php
+
+/**
+ * @file
+ * Helper module for testing EntityFieldQuery access on any type of entity.
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function entity_query_access_test_menu() {
+ $items['entity-query-access/test/%'] = array(
+ 'title' => "Retrieve a sample of entity query access data",
+ 'page callback' => 'entity_query_access_test_sample_query',
+ 'page arguments' => array(2),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
+ return $items;
+}
+
+/**
+ * Returns the results from an example EntityFieldQuery.
+ */
+function entity_query_access_test_sample_query($field_name) {
+ global $user;
+
+ // Simulate user does not have access to view all nodes.
+ $access = &drupal_static('node_access_view_all_nodes');
+ $access[$user->uid] = FALSE;
+
+ $query = new EntityFieldQuery();
+ $query
+ ->entityCondition('entity_type', 'test_entity_bundle_key')
+ ->fieldCondition($field_name, 'value', 0, '>')
+ ->entityOrderBy('entity_id', 'ASC');
+ $results = array(
+ 'items' => array(),
+ 'title' => t('EntityFieldQuery results'),
+ );
+ foreach ($query->execute() as $entity_type => $entity_ids) {
+ foreach ($entity_ids as $entity_id => $entity_stub) {
+ $results['items'][] = format_string('Found entity of type @entity_type with id @entity_id', array('@entity_type' => $entity_type, '@entity_id' => $entity_id));
+ }
+ }
+ if (count($results['items']) > 0) {
+ $output = theme('item_list', $results);
+ }
+ else {
+ $output = 'No results found with EntityFieldQuery.';
+ }
+ return $output;
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/error.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/error.test
new file mode 100644
index 0000000..f946e82
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/error.test
@@ -0,0 +1,116 @@
+<?php
+
+/**
+ * Tests Drupal error and exception handlers.
+ */
+class DrupalErrorHandlerTestCase extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Drupal error handlers',
+ 'description' => 'Performs tests on the Drupal error and exception handler.',
+ 'group' => 'System',
+ );
+ }
+
+ function setUp() {
+ parent::setUp('error_test');
+ }
+
+ /**
+ * Test the error handler.
+ */
+ function testErrorHandler() {
+ $error_notice = array(
+ '%type' => 'Notice',
+ '!message' => 'Undefined variable: bananas',
+ '%function' => 'error_test_generate_warnings()',
+ '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+ );
+ $error_warning = array(
+ '%type' => 'Warning',
+ '!message' => 'Division by zero',
+ '%function' => 'error_test_generate_warnings()',
+ '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+ );
+ $error_user_notice = array(
+ '%type' => 'User warning',
+ '!message' => 'Drupal is awesome',
+ '%function' => 'error_test_generate_warnings()',
+ '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+ );
+
+ // Set error reporting to collect notices.
+ variable_set('error_level', ERROR_REPORTING_DISPLAY_ALL);
+ $this->drupalGet('error-test/generate-warnings');
+ $this->assertResponse(200, 'Received expected HTTP status code.');
+ $this->assertErrorMessage($error_notice);
+ $this->assertErrorMessage($error_warning);
+ $this->assertErrorMessage($error_user_notice);
+
+ // Set error reporting to not collect notices.
+ variable_set('error_level', ERROR_REPORTING_DISPLAY_SOME);
+ $this->drupalGet('error-test/generate-warnings');
+ $this->assertResponse(200, 'Received expected HTTP status code.');
+ $this->assertNoErrorMessage($error_notice);
+ $this->assertErrorMessage($error_warning);
+ $this->assertErrorMessage($error_user_notice);
+
+ // Set error reporting to not show any errors.
+ variable_set('error_level', ERROR_REPORTING_HIDE);
+ $this->drupalGet('error-test/generate-warnings');
+ $this->assertResponse(200, 'Received expected HTTP status code.');
+ $this->assertNoErrorMessage($error_notice);
+ $this->assertNoErrorMessage($error_warning);
+ $this->assertNoErrorMessage($error_user_notice);
+ }
+
+ /**
+ * Test the exception handler.
+ */
+ function testExceptionHandler() {
+ $error_exception = array(
+ '%type' => 'Exception',
+ '!message' => 'Drupal is awesome',
+ '%function' => 'error_test_trigger_exception()',
+ '%line' => 57,
+ '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+ );
+ $error_pdo_exception = array(
+ '%type' => 'PDOException',
+ '!message' => 'SELECT * FROM bananas_are_awesome',
+ '%function' => 'error_test_trigger_pdo_exception()',
+ '%line' => 65,
+ '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
+ );
+
+ $this->drupalGet('error-test/trigger-exception');
+ $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.');
+ $this->assertErrorMessage($error_exception);
+
+ $this->drupalGet('error-test/trigger-pdo-exception');
+ $this->assertTrue(strpos($this->drupalGetHeader(':status'), '500 Service unavailable (with message)'), 'Received expected HTTP status line.');
+ // We cannot use assertErrorMessage() since the extact error reported
+ // varies from database to database. Check that the SQL string is displayed.
+ $this->assertText($error_pdo_exception['%type'], format_string('Found %type in error page.', $error_pdo_exception));
+ $this->assertText($error_pdo_exception['!message'], format_string('Found !message in error page.', $error_pdo_exception));
+ $error_details = format_string('in %function (line ', $error_pdo_exception);
+ $this->assertRaw($error_details, format_string("Found '!message' in error page.", array('!message' => $error_details)));
+ }
+
+ /**
+ * Helper function: assert that the error message is found.
+ */
+ function assertErrorMessage(array $error) {
+ $message = t('%type: !message in %function (line ', $error);
+ $this->assertRaw($message, format_string('Found error message: !message.', array('!message' => $message)));
+ }
+
+ /**
+ * Helper function: assert that the error message is not found.
+ */
+ function assertNoErrorMessage(array $error) {
+ $message = t('%type: !message in %function (line ', $error);
+ $this->assertNoRaw($message, format_string('Did not find error message: !message.', array('!message' => $message)));
+ }
+}
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/error_test.info b/kolab.org/www/drupal-7.26/modules/simpletest/tests/error_test.info
new file mode 100644
index 0000000..36e08a1
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/error_test.info
@@ -0,0 +1,12 @@
+name = "Error test"
+description = "Support module for error and exception testing."
+package = Testing
+version = VERSION
+core = 7.x
+hidden = TRUE
+
+; Information added by Drupal.org packaging script on 2014-01-15
+version = "7.26"
+project = "drupal"
+datestamp = "1389815930"
+
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/error_test.module b/kolab.org/www/drupal-7.26/modules/simpletest/tests/error_test.module
new file mode 100644
index 0000000..d062cb0
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/error_test.module
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * Implements hook_menu().
+ */
+function error_test_menu() {
+ $items['error-test/generate-warnings'] = array(
+ 'title' => 'Generate warnings',
+ 'page callback' => 'error_test_generate_warnings',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['error-test/generate-warnings-with-report'] = array(
+ 'title' => 'Generate warnings with Simpletest reporting',
+ 'page callback' => 'error_test_generate_warnings',
+ 'page arguments' => array(TRUE),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['error-test/trigger-exception'] = array(
+ 'title' => 'Trigger an exception',
+ 'page callback' => 'error_test_trigger_exception',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['error-test/trigger-pdo-exception'] = array(
+ 'title' => 'Trigger a PDO exception',
+ 'page callback' => 'error_test_trigger_pdo_exception',
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+
+ return $items;
+}
+
+/**
+ * Menu callback; generate warnings to test the error handler.
+ */
+function error_test_generate_warnings($collect_errors = FALSE) {
+ // Tell Drupal error reporter to send errors to Simpletest or not.
+ define('SIMPLETEST_COLLECT_ERRORS', $collect_errors);
+ // This will generate a notice.
+ $monkey_love = $bananas;
+ // This will generate a warning.
+ $awesomely_big = 1/0;
+ // This will generate a user error.
+ trigger_error("Drupal is awesome", E_USER_WARNING);
+ return "";
+}
+
+/**
+ * Menu callback; trigger an exception to test the exception handler.
+ */
+function error_test_trigger_exception() {
+ define('SIMPLETEST_COLLECT_ERRORS', FALSE);
+ throw new Exception("Drupal is awesome");
+}
+
+/**
+ * Menu callback; trigger an exception to test the exception handler.
+ */
+function error_test_trigger_pdo_exception() {
+ define('SIMPLETEST_COLLECT_ERRORS', FALSE);
+ db_query('SELECT * FROM bananas_are_awesome');
+}
diff --git a/kolab.org/www/drupal-7.26/modules/simpletest/tests/file.test b/kolab.org/www/drupal-7.26/modules/simpletest/tests/file.test
new file mode 100644
index 0000000..20dd273
--- /dev/null
+++ b/kolab.org/www/drupal-7.26/modules/simpletest/tests/file.test
@@ -0,0 +1,2782 @@
+<?php
+
+/**
+ * @file
+ * This provides SimpleTests for the core file handling functionality.
+ * These include FileValidateTest and FileSaveTest.
+ */
+
+/**
+ * Helper validator that returns the $errors parameter.
+ */
+function file_test_validator($file, $errors) {
+ return $errors;
+}
+
+/**
+ * Helper function for testing file_scan_directory().
+ *
+ * Each time the function is called the file is stored in a static variable.
+ * When the function is called with no $filepath parameter, the results are
+ * returned.
+ *
+ * @param $filepath
+ * File path
+ * @return
+ * If $filepath is NULL, an array of all previous $filepath parameters
+ */
+function file_test_file_scan_callback($filepath = NULL) {
+ $files = &drupal_static(__FUNCTION__, array());
+ if (isset($filepath)) {
+ $files[] = $filepath;
+ }
+ else {
+ return $files;
+ }
+}
+
+/**
+ * Reset static variables used by file_test_file_scan_callback().
+ */
+function file_test_file_scan_callback_reset() {
+ drupal_static_reset('file_test_file_scan_callback');
+}
+
+/**
+ * Base class for file tests that adds some additional file specific
+ * assertions and helper functions.
+ */
+class FileTestCase extends DrupalWebTestCase {
+ /**
+ * Check that two files have the same values for all fields other than the
+ * timestamp.
+ *
+ * @param $before
+ * File object to compare.
+ * @param $after
+ * File object to compare.
+ */
+ function assertFileUnchanged($before, $after) {
+ $this->assertEqual($before->fid, $after->fid, format_string('File id is the same: %file1 == %file2.', array('%file1' => $before->fid, '%file2' => $after->fid)), 'File unchanged');
+ $this->assertEqual($before->uid, $after->uid, format_string('File owner is the same: %file1 == %file2.', array('%file1' => $before->uid, '%file2' => $after->uid)), 'File unchanged');
+ $this->assertEqual($before->filename, $after->filename, format_string('File name is the same: %file1 == %file2.', array('%file1' => $before->filename, '%file2' => $after->filename)), 'File unchanged');
+ $this->assertEqual($before->uri, $after->uri, format_string('File path is the same: %file1 == %file2.', array('%file1' => $before->uri, '%file2' => $after->uri)), 'File unchanged');
+ $this->assertEqual($before->filemime, $after->filemime, format_string('File MIME type is the same: %file1 == %file2.', array('%file1' => $before->filemime, '%file2' => $after->filemime)), 'File unchanged');
+ $this->assertEqual($before->filesize, $after->filesize, format_string('File size is the same: %file1 == %file2.', array('%file1' => $before->filesize, '%file2' => $after->filesize)), 'File unchanged');
+ $this->assertEqual($before->status, $after->status, format_string('File status is the same: %file1 == %file2.', array('%file1' => $before->status, '%file2' => $after->status)), 'File unchanged');
+ }
+
+ /**
+ * Check that two files are not the same by comparing the fid and filepath.
+ *
+ * @param $file1
+ * File object to compare.
+ * @param $file2
+ * File object to compare.
+ */
+ function assertDifferentFile($file1, $file2) {
+ $this->assertNotEqual($file1->fid, $file2->fid, format_string('Files have different ids: %file1 != %file2.', array('%file1' => $file1->fid, '%file2' => $file2->fid)), 'Different file');
+ $this->assertNotEqual($file1->uri, $file2->uri, format_string('Files have different paths: %file1 != %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Different file');
+ }
+
+ /**
+ * Check that two files are the same by comparing the fid and filepath.
+ *
+ * @param $file1
+ * File object to compare.
+ * @param $file2
+ * File object to compare.
+ */
+ function assertSameFile($file1, $file2) {
+ $this->assertEqual($file1->fid, $file2->fid, format_string('Files have the same ids: %file1 == %file2.', array('%file1' => $file1->fid, '%file2-fid' => $file2->fid)), 'Same file');
+ $this->assertEqual($file1->uri, $file2->uri, format_string('Files have the same path: %file1 == %file2.', array('%file1' => $file1->uri, '%file2' => $file2->uri)), 'Same file');
+ }
+
+ /**
+ * Helper function to test the permissions of a file.
+ *
+ * @param $filepath
+ * String file path.
+ * @param $expected_mode
+ * Octal integer like 0664 or 0777.
+ * @param $message
+ * Optional message.
+ */
+ function assertFilePermissions($filepath, $expected_mode, $message = NULL) {
+ // Clear out PHP's file stat cache to be sure we see the current value.
+ clearstatcache();
+
+ // Mask out all but the last three octets.
+ $actual_mode = fileperms($filepath) & 0777;
+
+ // PHP on Windows has limited support for file permissions. Usually each of
+ // "user", "group" and "other" use one octal digit (3 bits) to represent the
+ // read/write/execute bits. On Windows, chmod() ignores the "group" and
+ // "other" bits, and fileperms() returns the "user" bits in all three
+ // positions. $expected_mode is updated to reflect this.
+ if (substr(PHP_OS, 0, 3) == 'WIN') {
+ // Reset the "group" and "other" bits.
+ $expected_mode = $expected_mode & 0700;
+ // Shift the "user" bits to the "group" and "other" positions also.
+ $expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6;
+ }
+
+ if (!isset($message)) {
+ $message = t('Expected file permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
+ }
+ $this->assertEqual($actual_mode, $expected_mode, $message);
+ }
+
+ /**
+ * Helper function to test the permissions of a directory.
+ *
+ * @param $directory
+ * String directory path.
+ * @param $expected_mode
+ * Octal integer like 0664 or 0777.
+ * @param $message
+ * Optional message.
+ */
+ function assertDirectoryPermissions($directory, $expected_mode, $message = NULL) {
+ // Clear out PHP's file stat cache to be sure we see the current value.
+ clearstatcache();
+
+ // Mask out all but the last three octets.
+ $actual_mode = fileperms($directory) & 0777;
+
+ // PHP on Windows has limited support for file permissions. Usually each of
+ // "user", "group" and "other" use one octal digit (3 bits) to represent the
+ // read/write/execute bits. On Windows, chmod() ignores the "group" and
+ // "other" bits, and fileperms() returns the "user" bits in all three
+ // positions. $expected_mode is updated to reflect this.
+ if (substr(PHP_OS, 0, 3) == 'WIN') {
+ // Reset the "group" and "other" bits.
+ $expected_mode = $expected_mode & 0700;
+ // Shift the "user" bits to the "group" and "other" positions also.
+ $expected_mode = $expected_mode | $expected_mode >> 3 | $expected_mode >> 6;
+ }
+
+ if (!isset($message)) {
+ $message = t('Expected directory permission to be %expected, actually were %actual.', array('%actual' => decoct($actual_mode), '%expected' => decoct($expected_mode)));
+ }
+ $this->assertEqual($actual_mode, $expected_mode, $message);
+ }
+
+ /**
+ * Create a directory and assert it exists.
+ *
+ * @param $path
+ * Optional string with a directory path. If none is provided, a random
+ * name in the site's files directory will be used.
+ * @return
+ * The path to the directory.
+ */
+ function createDirectory($path = NULL) {
+ // A directory to operate on.
+ if (!isset($path)) {
+ $path = file_default_scheme() . '://' . $this->randomName();
+ }
+ $this->assertTrue(drupal_mkdir($path) && is_dir($path), 'Directory was created successfully.');
+ return $path;
+ }
+
+ /**
+ * Create a file and save it to the files table and assert that it occurs
+ * correctly.
+ *
+ * @param $filepath
+ * Optional string specifying the file path. If none is provided then a
+ * randomly named file will be created in the site's files directory.
+ * @param $contents
+ * Optional contents to save into the file. If a NULL value is provided an
+ * arbitrary string will be used.
+ * @param $scheme
+ * Optional string indicating the stream scheme to use. Drupal core includes
+ * public, private, and temporary. The public wrapper is the default.
+ * @return
+ * File object.
+ */
+ function createFile($filepath = NULL, $contents = NULL, $scheme = NULL) {
+ if (!isset($filepath)) {
+ // Prefix with non-latin characters to ensure that all file-related
+ // tests work with international filenames.
+ $filepath = 'Файл для тестирования ' . $this->randomName();
+ }
+ if (!isset($scheme)) {
+ $scheme = file_default_scheme();
+ }
+ $filepath = $scheme . '://' . $filepath;
+
+ if (!isset($contents)) {
+ $contents = "file_put_contents() doesn't seem to appreciate empty strings so let's put in some data.";
+ }
+
+ file_put_contents($filepath, $contents);
+ $this->assertTrue(is_file($filepath), 'The test file exists on the disk.', 'Create test file');
+
+ $file = new stdClass();
+ $file->uri = $filepath;
+ $file->filename = drupal_basename($file->uri);
+ $file->filemime = 'text/plain';
+ $file->uid = 1;
+ $file->timestamp = REQUEST_TIME;
+ $file->filesize = filesize($file->uri);
+ $file->status = 0;
+ // Write the record directly rather than calling file_save() so we don't
+ // invoke the hooks.
+ $this->assertNotIdentical(drupal_write_record('file_managed', $file), FALSE, 'The file was added to the database.', 'Create test file');
+
+ return $file;
+ }
+}
+
+/**
+ * Base class for file tests that use the file_test module to test uploads and
+ * hooks.
+ */
+class FileHookTestCase extends FileTestCase {
+ function setUp() {
+ // Install file_test module
+ parent::setUp('file_test');
+ // Clear out any hook calls.
+ file_test_reset();
+ }
+
+ /**
+ * Assert that all of the specified hook_file_* hooks were called once, other
+ * values result in failure.
+ *
+ * @param $expected
+ * Array with string containing with the hook name, e.g. 'load', 'save',
+ * 'insert', etc.
+ */
+ function assertFileHooksCalled($expected) {
+ // Determine which hooks were called.
+ $actual = array_keys(array_filter(file_test_get_all_calls()));
+
+ // Determine if there were any expected that were not called.
+ $uncalled = array_diff($expected, $actual);
+ if (count($uncalled)) {
+ $this->assertTrue(FALSE, format_string('Expected hooks %expected to be called but %uncalled was not called.', array('%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled))));
+ }
+ else {
+ $this->assertTrue(TRUE, format_string('All the expected hooks were called: %expected', array('%expected' => empty($expected) ? t('(none)') : implode(', ', $expected))));
+ }
+
+ // Determine if there were any unexpected calls.
+ $unexpected = array_diff($actual, $expected);
+ if (count($unexpected)) {
+ $this->assertTrue(FALSE, format_string('Unexpected hooks were called: %unexpected.', array('%unexpected' => empty($unexpected) ? t('(none)') : implode(', ', $unexpected))));
+ }
+ else {
+ $this->assertTrue(TRUE, 'No unexpected hooks were called.');
+ }
+ }
+
+ /**
+ * Assert that a hook_file_* hook was called a certain number of times.
+ *
+ * @param $hook
+ * String with the hook name, e.g. 'load', 'save', 'insert', etc.
+ * @param $expected_count
+ * Optional integer count.
+ * @param $message
+ * Optional translated string message.
+ */
+ function assertFileHookCalled($hook, $expected_count = 1, $message = NULL) {
+ $actual_count = count(file_test_get_calls($hook));
+
+ if (!isset($message)) {
+ if ($actual_count == $expected_count) {
+ $message = format_string('hook_file_@name was called correctly.', array('@name' => $hook));
+ }
+ elseif ($expected_count == 0) {
+ $message = format_plural($actual_count, 'hook_file_@name was not expected to be called but was actually called once.', 'hook_file_@name was not expected to be called but was actually called @count times.', array('@name' => $hook, '@count' => $actual_count));
+ }
+ else {
+ $message = format_string('hook_file_@name was expected to be called %expected times but was called %actual times.', array('@name' => $hook, '%expected' => $expected_count, '%actual' => $actual_count));
+ }
+ }
+ $this->assertEqual($actual_count, $expected_count, $message);
+ }
+}
+
+
+/**
+ * This will run tests against the file_space_used() function.
+ */
+class FileSpaceUsedTest extends FileTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File space used tests',
+ 'description' => 'Tests the file_space_used() function.',
+ 'group' => 'File API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ // Create records for a couple of users with different sizes.
+ $file = array('uid' => 2, 'uri' => 'public://example1.txt', 'filesize' => 50, 'status' => FILE_STATUS_PERMANENT);
+ drupal_write_record('file_managed', $file);
+ $file = array('uid' => 2, 'uri' => 'public://example2.txt', 'filesize' => 20, 'status' => FILE_STATUS_PERMANENT);
+ drupal_write_record('file_managed', $file);
+ $file = array('uid' => 3, 'uri' => 'public://example3.txt', 'filesize' => 100, 'status' => FILE_STATUS_PERMANENT);
+ drupal_write_record('file_managed', $file);
+ $file = array('uid' => 3, 'uri' => 'public://example4.txt', 'filesize' => 200, 'status' => FILE_STATUS_PERMANENT);
+ drupal_write_record('file_managed', $file);
+
+ // Now create some non-permanent files.
+ $file = array('uid' => 2, 'uri' => 'public://example5.txt', 'filesize' => 1, 'status' => 0);
+ drupal_write_record('file_managed', $file);
+ $file = array('uid' => 3, 'uri' => 'public://example6.txt', 'filesize' => 3, 'status' => 0);
+ drupal_write_record('file_managed', $file);
+ }
+
+ /**
+ * Test different users with the default status.
+ */
+ function testFileSpaceUsed() {
+ // Test different users with default status.
+ $this->assertEqual(file_space_used(2), 70);
+ $this->assertEqual(file_space_used(3), 300);
+ $this->assertEqual(file_space_used(), 370);
+
+ // Test the status fields
+ $this->assertEqual(file_space_used(NULL, 0), 4);
+ $this->assertEqual(file_space_used(NULL, FILE_STATUS_PERMANENT), 370);
+
+ // Test both the user and status.
+ $this->assertEqual(file_space_used(1, 0), 0);
+ $this->assertEqual(file_space_used(1, FILE_STATUS_PERMANENT), 0);
+ $this->assertEqual(file_space_used(2, 0), 1);
+ $this->assertEqual(file_space_used(2, FILE_STATUS_PERMANENT), 70);
+ $this->assertEqual(file_space_used(3, 0), 3);
+ $this->assertEqual(file_space_used(3, FILE_STATUS_PERMANENT), 300);
+ }
+}
+
+/**
+ * This will run tests against the file validation functions (file_validate_*).
+ */
+class FileValidatorTest extends DrupalWebTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File validator tests',
+ 'description' => 'Tests the functions used to validate uploaded files.',
+ 'group' => 'File API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+
+ $this->image = new stdClass();
+ $this->image->uri = 'misc/druplicon.png';
+ $this->image->filename = drupal_basename($this->image->uri);
+
+ $this->non_image = new stdClass();
+ $this->non_image->uri = 'misc/jquery.js';
+ $this->non_image->filename = drupal_basename($this->non_image->uri);
+ }
+
+ /**
+ * Test the file_validate_extensions() function.
+ */
+ function testFileValidateExtensions() {
+ $file = new stdClass();
+ $file->filename = 'asdf.txt';
+ $errors = file_validate_extensions($file, 'asdf txt pork');
+ $this->assertEqual(count($errors), 0, 'Valid extension accepted.', 'File');
+
+ $file->filename = 'asdf.txt';
+ $errors = file_validate_extensions($file, 'exe png');
+ $this->assertEqual(count($errors), 1, 'Invalid extension blocked.', 'File');
+ }
+
+ /**
+ * This ensures a specific file is actually an image.
+ */
+ function testFileValidateIsImage() {
+ $this->assertTrue(file_exists($this->image->uri), 'The image being tested exists.', 'File');
+ $errors = file_validate_is_image($this->image);
+ $this->assertEqual(count($errors), 0, 'No error reported for our image file.', 'File');
+
+ $this->assertTrue(file_exists($this->non_image->uri), 'The non-image being tested exists.', 'File');
+ $errors = file_validate_is_image($this->non_image);
+ $this->assertEqual(count($errors), 1, 'An error reported for our non-image file.', 'File');
+ }
+
+ /**
+ * This ensures the resolution of a specific file is within bounds.
+ * The image will be resized if it's too large.
+ */
+ function testFileValidateImageResolution() {
+ // Non-images.
+ $errors = file_validate_image_resolution($this->non_image);
+ $this->assertEqual(count($errors), 0, 'Should not get any errors for a non-image file.', 'File');
+ $errors = file_validate_image_resolution($this->non_image, '50x50', '100x100');
+ $this->assertEqual(count($errors), 0, 'Do not check the resolution on non files.', 'File');
+
+ // Minimum size.
+ $errors = file_validate_image_resolution($this->image);
+ $this->assertEqual(count($errors), 0, 'No errors for an image when there is no minimum or maximum resolution.', 'File');
+ $errors = file_validate_image_resolution($this->image, 0, '200x1');
+ $this->assertEqual(count($errors), 1, 'Got an error for an image that was not wide enough.', 'File');
+ $errors = file_validate_image_resolution($this->image, 0, '1x200');
+ $this->assertEqual(count($errors), 1, 'Got an error for an image that was not tall enough.', 'File');
+ $errors = file_validate_image_resolution($this->image, 0, '200x200');
+ $this->assertEqual(count($errors), 1, 'Small images report an error.', 'File');
+
+ // Maximum size.
+ if (image_get_toolkit()) {
+ // Copy the image so that the original doesn't get resized.
+ copy('misc/druplicon.png', 'temporary://druplicon.png');
+ $this->image->uri = 'temporary://druplicon.png';
+
+ $errors = file_validate_image_resolution($this->image, '10x5');
+ $this->assertEqual(count($errors), 0, 'No errors should be reported when an oversized image can be scaled down.', 'File');
+
+ $info = image_get_info($this->image->uri);
+ $this->assertTrue($info['width'] <= 10, 'Image scaled to correct width.', 'File');
+ $this->assertTrue($info['height'] <= 5, 'Image scaled to correct height.', 'File');
+
+ drupal_unlink('temporary://druplicon.png');
+ }
+ else {
+ // TODO: should check that the error is returned if no toolkit is available.
+ $errors = file_validate_image_resolution($this->image, '5x10');
+ $this->assertEqual(count($errors), 1, 'Oversize images that cannot be scaled get an error.', 'File');
+ }
+ }
+
+ /**
+ * This will ensure the filename length is valid.
+ */
+ function testFileValidateNameLength() {
+ // Create a new file object.
+ $file = new stdClass();
+
+ // Add a filename with an allowed length and test it.
+ $file->filename = str_repeat('x', 240);
+ $this->assertEqual(strlen($file->filename), 240);
+ $errors = file_validate_name_length($file);
+ $this->assertEqual(count($errors), 0, 'No errors reported for 240 length filename.', 'File');
+
+ // Add a filename with a length too long and test it.
+ $file->filename = str_repeat('x', 241);
+ $errors = file_validate_name_length($file);
+ $this->assertEqual(count($errors), 1, 'An error reported for 241 length filename.', 'File');
+
+ // Add a filename with an empty string and test it.
+ $file->filename = '';
+ $errors = file_validate_name_length($file);
+ $this->assertEqual(count($errors), 1, 'An error reported for 0 length filename.', 'File');
+ }
+
+
+ /**
+ * Test file_validate_size().
+ */
+ function testFileValidateSize() {
+ global $user;
+ $original_user = $user;
+ drupal_save_session(FALSE);
+
+ // Run these test as uid = 1.
+ $user = user_load(1);
+
+ $file = new stdClass();
+ $file->filesize = 999999;
+ $errors = file_validate_size($file, 1, 1);
+ $this->assertEqual(count($errors), 0, 'No size limits enforced on uid=1.', 'File');
+
+ // Run these tests as a regular user.
+ $user = $this->drupalCreateUser();
+
+ // Create a file with a size of 1000 bytes, and quotas of only 1 byte.
+ $file = new stdClass();
+ $file->filesize = 1000;
+ $errors = file_validate_size($file, 0, 0);
+ $this->assertEqual(count($errors), 0, 'No limits means no errors.', 'File');
+ $errors = file_validate_size($file, 1, 0);
+ $this->assertEqual(count($errors), 1, 'Error for the file being over the limit.', 'File');
+ $errors = file_validate_size($file, 0, 1);
+ $this->assertEqual(count($errors), 1, 'Error for the user being over their limit.', 'File');
+ $errors = file_validate_size($file, 1, 1);
+ $this->assertEqual(count($errors), 2, 'Errors for both the file and their limit.', 'File');
+
+ $user = $original_user;
+ drupal_save_session(TRUE);
+ }
+}
+
+
+
+/**
+ * Tests the file_unmanaged_save_data() function.
+ */
+class FileUnmanagedSaveDataTest extends FileTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Unmanaged file save data',
+ 'description' => 'Tests the unmanaged file save data function.',
+ 'group' => 'File API',
+ );
+ }
+
+ /**
+ * Test the file_unmanaged_save_data() function.
+ */
+ function testFileSaveData() {
+ $contents = $this->randomName(8);
+
+ // No filename.
+ $filepath = file_unmanaged_save_data($contents);
+ $this->assertTrue($filepath, 'Unnamed file saved correctly.');
+ $this->assertEqual(file_uri_scheme($filepath), file_default_scheme(), "File was placed in Drupal's files directory.");
+ $this->assertEqual($contents, file_get_contents($filepath), 'Contents of the file are correct.');
+
+ // Provide a filename.
+ $filepath = file_unmanaged_save_data($contents, 'public://asdf.txt', FILE_EXISTS_REPLACE);
+ $this->assertTrue($filepath, 'Unnamed file saved correctly.');
+ $this->assertEqual('asdf.txt', drupal_basename($filepath), 'File was named correctly.');
+ $this->assertEqual($contents, file_get_contents($filepath), 'Contents of the file are correct.');
+ $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664));
+ }
+}
+
+/**
+ * Tests the file_unmanaged_save_data() function on remote filesystems.
+ */
+class RemoteFileUnmanagedSaveDataTest extends FileUnmanagedSaveDataTest {
+ public static function getInfo() {
+ $info = parent::getInfo();
+ $info['group'] = 'File API (remote)';
+ return $info;
+ }
+
+ function setUp() {
+ parent::setUp('file_test');
+ variable_set('file_default_scheme', 'dummy-remote');
+ }
+}
+
+/**
+ * Test the file_save_upload() function.
+ */
+class FileSaveUploadTest extends FileHookTestCase {
+ /**
+ * An image file path for uploading.
+ */
+ protected $image;
+
+ /**
+ * A PHP file path for upload security testing.
+ */
+ protected $phpfile;
+
+ /**
+ * The largest file id when the test starts.
+ */
+ protected $maxFidBefore;
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'File uploading',
+ 'description' => 'Tests the file uploading functions.',
+ 'group' => 'File API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+ $account = $this->drupalCreateUser(array('access content'));
+ $this->drupalLogin($account);
+
+ $image_files = $this->drupalGetTestFiles('image');
+ $this->image = current($image_files);
+
+ list(, $this->image_extension) = explode('.', $this->image->filename);
+ $this->assertTrue(is_file($this->image->uri), "The image file we're going to upload exists.");
+
+ $this->phpfile = current($this->drupalGetTestFiles('php'));
+ $this->assertTrue(is_file($this->phpfile->uri), "The PHP file we're going to upload exists.");
+
+ $this->maxFidBefore = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
+
+ // Upload with replace to guarantee there's something there.
+ $edit = array(
+ 'file_test_replace' => FILE_EXISTS_REPLACE,
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri),
+ );
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called then clean out the hook
+ // counters.
+ $this->assertFileHooksCalled(array('validate', 'insert'));
+ file_test_reset();
+ }
+
+ /**
+ * Test the file_save_upload() function.
+ */
+ function testNormal() {
+ $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
+ $this->assertTrue($max_fid_after > $this->maxFidBefore, 'A new file was created.');
+ $file1 = file_load($max_fid_after);
+ $this->assertTrue($file1, 'Loaded the file.');
+ // MIME type of the uploaded image may be either image/jpeg or image/png.
+ $this->assertEqual(substr($file1->filemime, 0, 5), 'image', 'A MIME type was set.');
+
+ // Reset the hook counters to get rid of the 'load' we just called.
+ file_test_reset();
+
+ // Upload a second file.
+ $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
+ $image2 = current($this->drupalGetTestFiles('image'));
+ $edit = array('files[file_test_upload]' => drupal_realpath($image2->uri));
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertRaw(t('You WIN!'));
+ $max_fid_after = db_query('SELECT MAX(fid) AS fid FROM {file_managed}')->fetchField();
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'insert'));
+
+ $file2 = file_load($max_fid_after);
+ $this->assertTrue($file2);
+ // MIME type of the uploaded image may be either image/jpeg or image/png.
+ $this->assertEqual(substr($file2->filemime, 0, 5), 'image', 'A MIME type was set.');
+
+ // Load both files using file_load_multiple().
+ $files = file_load_multiple(array($file1->fid, $file2->fid));
+ $this->assertTrue(isset($files[$file1->fid]), 'File was loaded successfully');
+ $this->assertTrue(isset($files[$file2->fid]), 'File was loaded successfully');
+
+ // Upload a third file to a subdirectory.
+ $image3 = current($this->drupalGetTestFiles('image'));
+ $image3_realpath = drupal_realpath($image3->uri);
+ $dir = $this->randomName();
+ $edit = array(
+ 'files[file_test_upload]' => $image3_realpath,
+ 'file_subdir' => $dir,
+ );
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertRaw(t('You WIN!'));
+ $this->assertTrue(is_file('temporary://' . $dir . '/' . trim(drupal_basename($image3_realpath))));
+
+ // Check that file_load_multiple() with no arguments returns FALSE.
+ $this->assertFalse(file_load_multiple(), 'No files were loaded.');
+ }
+
+ /**
+ * Test extension handling.
+ */
+ function testHandleExtension() {
+ // The file being tested is a .gif which is in the default safe list
+ // of extensions to allow when the extension validator isn't used. This is
+ // implicitly tested at the testNormal() test. Here we tell
+ // file_save_upload() to only allow ".foo".
+ $extensions = 'foo';
+ $edit = array(
+ 'file_test_replace' => FILE_EXISTS_REPLACE,
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri),
+ 'extensions' => $extensions,
+ );
+
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $message = t('Only files with the following extensions are allowed:') . ' <em class="placeholder">' . $extensions . '</em>';
+ $this->assertRaw($message, 'Cannot upload a disallowed extension');
+ $this->assertRaw(t('Epic upload FAIL!'), 'Found the failure message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate'));
+
+ // Reset the hook counters.
+ file_test_reset();
+
+ $extensions = 'foo ' . $this->image_extension;
+ // Now tell file_save_upload() to allow the extension of our test image.
+ $edit = array(
+ 'file_test_replace' => FILE_EXISTS_REPLACE,
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri),
+ 'extensions' => $extensions,
+ );
+
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertNoRaw(t('Only files with the following extensions are allowed:'), 'Can upload an allowed extension.');
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'load', 'update'));
+
+ // Reset the hook counters.
+ file_test_reset();
+
+ // Now tell file_save_upload() to allow any extension.
+ $edit = array(
+ 'file_test_replace' => FILE_EXISTS_REPLACE,
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri),
+ 'allow_all_extensions' => TRUE,
+ );
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertNoRaw(t('Only files with the following extensions are allowed:'), 'Can upload any extension.');
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'load', 'update'));
+ }
+
+ /**
+ * Test dangerous file handling.
+ */
+ function testHandleDangerousFile() {
+ // Allow the .php extension and make sure it gets renamed to .txt for
+ // safety. Also check to make sure its MIME type was changed.
+ $edit = array(
+ 'file_test_replace' => FILE_EXISTS_REPLACE,
+ 'files[file_test_upload]' => drupal_realpath($this->phpfile->uri),
+ 'is_image_file' => FALSE,
+ 'extensions' => 'php',
+ );
+
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $message = t('For security reasons, your upload has been renamed to') . ' <em class="placeholder">' . $this->phpfile->filename . '.txt' . '</em>';
+ $this->assertRaw($message, 'Dangerous file was renamed.');
+ $this->assertRaw(t('File MIME type is text/plain.'), "Dangerous file's MIME type was changed.");
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'insert'));
+
+ // Ensure dangerous files are not renamed when insecure uploads is TRUE.
+ // Turn on insecure uploads.
+ variable_set('allow_insecure_uploads', 1);
+ // Reset the hook counters.
+ file_test_reset();
+
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertNoRaw(t('For security reasons, your upload has been renamed'), 'Found no security message.');
+ $this->assertRaw(t('File name is !filename', array('!filename' => $this->phpfile->filename)), 'Dangerous file was not renamed when insecure uploads is TRUE.');
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'insert'));
+
+ // Turn off insecure uploads.
+ variable_set('allow_insecure_uploads', 0);
+ }
+
+ /**
+ * Test file munge handling.
+ */
+ function testHandleFileMunge() {
+ // Ensure insecure uploads are disabled for this test.
+ variable_set('allow_insecure_uploads', 0);
+ $this->image = file_move($this->image, $this->image->uri . '.foo.' . $this->image_extension);
+
+ // Reset the hook counters to get rid of the 'move' we just called.
+ file_test_reset();
+
+ $extensions = $this->image_extension;
+ $edit = array(
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri),
+ 'extensions' => $extensions,
+ );
+
+ $munged_filename = $this->image->filename;
+ $munged_filename = substr($munged_filename, 0, strrpos($munged_filename, '.'));
+ $munged_filename .= '_.' . $this->image_extension;
+
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertRaw(t('For security reasons, your upload has been renamed'), 'Found security message.');
+ $this->assertRaw(t('File name is !filename', array('!filename' => $munged_filename)), 'File was successfully munged.');
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'insert'));
+
+ // Ensure we don't munge files if we're allowing any extension.
+ // Reset the hook counters.
+ file_test_reset();
+
+ $edit = array(
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri),
+ 'allow_all_extensions' => TRUE,
+ );
+
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertNoRaw(t('For security reasons, your upload has been renamed'), 'Found no security message.');
+ $this->assertRaw(t('File name is !filename', array('!filename' => $this->image->filename)), 'File was not munged when allowing any extension.');
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'insert'));
+ }
+
+ /**
+ * Test renaming when uploading over a file that already exists.
+ */
+ function testExistingRename() {
+ $edit = array(
+ 'file_test_replace' => FILE_EXISTS_RENAME,
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri)
+ );
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'insert'));
+ }
+
+ /**
+ * Test replacement when uploading over a file that already exists.
+ */
+ function testExistingReplace() {
+ $edit = array(
+ 'file_test_replace' => FILE_EXISTS_REPLACE,
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri)
+ );
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertRaw(t('You WIN!'), 'Found the success message.');
+
+ // Check that the correct hooks were called.
+ $this->assertFileHooksCalled(array('validate', 'load', 'update'));
+ }
+
+ /**
+ * Test for failure when uploading over a file that already exists.
+ */
+ function testExistingError() {
+ $edit = array(
+ 'file_test_replace' => FILE_EXISTS_ERROR,
+ 'files[file_test_upload]' => drupal_realpath($this->image->uri)
+ );
+ $this->drupalPost('file-test/upload', $edit, t('Submit'));
+ $this->assertResponse(200, 'Received a 200 response for posted test file.');
+ $this->assertRaw(t('Epic upload FAIL!'), 'Found the failure message.');
+
+ // Check that the no hooks were called while failing.
+ $this->assertFileHooksCalled(array());
+ }
+
+ /**
+ * Test for no failures when not uploading a file.
+ */
+ function testNoUpload() {
+ $this->drupalPost('file-test/upload', array(), t('Submit'));
+ $this->assertNoRaw(t('Epic upload FAIL!'), 'Failure message not found.');
+ }
+}
+
+/**
+ * Test the file_save_upload() function on remote filesystems.
+ */
+class RemoteFileSaveUploadTest extends FileSaveUploadTest {
+ public static function getInfo() {
+ $info = parent::getInfo();
+ $info['group'] = 'File API (remote)';
+ return $info;
+ }
+
+ function setUp() {
+ parent::setUp('file_test');
+ variable_set('file_default_scheme', 'dummy-remote');
+ }
+}
+
+/**
+ * Directory related tests.
+ */
+class FileDirectoryTest extends FileTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File paths and directories',
+ 'description' => 'Tests operations dealing with directories.',
+ 'group' => 'File API',
+ );
+ }
+
+ /**
+ * Test directory handling functions.
+ */
+ function testFileCheckDirectoryHandling() {
+ // A directory to operate on.
+ $directory = file_default_scheme() . '://' . $this->randomName() . '/' . $this->randomName();
+ $this->assertFalse(is_dir($directory), 'Directory does not exist prior to testing.');
+
+ // Non-existent directory.
+ $this->assertFalse(file_prepare_directory($directory, 0), 'Error reported for non-existing directory.', 'File');
+
+ // Make a directory.
+ $this->assertTrue(file_prepare_directory($directory, FILE_CREATE_DIRECTORY), 'No error reported when creating a new directory.', 'File');
+
+ // Make sure directory actually exists.
+ $this->assertTrue(is_dir($directory), 'Directory actually exists.', 'File');
+
+ if (substr(PHP_OS, 0, 3) != 'WIN') {
+ // PHP on Windows doesn't support any kind of useful read-only mode for
+ // directories. When executing a chmod() on a directory, PHP only sets the
+ // read-only flag, which doesn't prevent files to actually be written
+ // in the directory on any recent version of Windows.
+
+ // Make directory read only.
+ @drupal_chmod($directory, 0444);
+ $this->assertFalse(file_prepare_directory($directory, 0), 'Error reported for a non-writeable directory.', 'File');
+
+ // Test directory permission modification.
+ $this->assertTrue(file_prepare_directory($directory, FILE_MODIFY_PERMISSIONS), 'No error reported when making directory writeable.', 'File');
+ }
+
+ // Test that the directory has the correct permissions.
+ $this->assertDirectoryPermissions($directory, variable_get('file_chmod_directory', 0775));
+
+ // Remove .htaccess file to then test that it gets re-created.
+ @drupal_unlink(file_default_scheme() . '://.htaccess');
+ $this->assertFalse(is_file(file_default_scheme() . '://.htaccess'), 'Successfully removed the .htaccess file in the files directory.', 'File');
+ file_ensure_htaccess();
+ $this->assertTrue(is_file(file_default_scheme() . '://.htaccess'), 'Successfully re-created the .htaccess file in the files directory.', 'File');
+ // Verify contents of .htaccess file.
+ $file = file_get_contents(file_default_scheme() . '://.htaccess');
+ $this->assertEqual($file, file_htaccess_lines(FALSE), 'The .htaccess file contains the proper content.', 'File');
+ }
+
+ /**
+ * This will take a directory and path, and find a valid filepath that is not
+ * taken by another file.
+ */
+ function testFileCreateNewFilepath() {
+ // First we test against an imaginary file that does not exist in a
+ // directory.
+ $basename = 'xyz.txt';
+ $directory = 'misc';
+ $original = $directory . '/' . $basename;
+ $path = file_create_filename($basename, $directory);
+ $this->assertEqual($path, $original, format_string('New filepath %new equals %original.', array('%new' => $path, '%original' => $original)), 'File');
+
+ // Then we test against a file that already exists within that directory.
+ $basename = 'druplicon.png';
+ $original = $directory . '/' . $basename;
+ $expected = $directory . '/druplicon_0.png';
+ $path = file_create_filename($basename, $directory);
+ $this->assertEqual($path, $expected, format_string('Creating a new filepath from %original equals %new.', array('%new' => $path, '%original' => $original)), 'File');
+
+ // @TODO: Finally we copy a file into a directory several times, to ensure a properly iterating filename suffix.
+ }
+
+ /**
+ * This will test the filepath for a destination based on passed flags and
+ * whether or not the file exists.
+ *
+ * If a file exists, file_destination($destination, $replace) will either
+ * return:
+ * - the existing filepath, if $replace is FILE_EXISTS_REPLACE
+ * - a new filepath if FILE_EXISTS_RENAME
+ * - an error (returning FALSE) if FILE_EXISTS_ERROR.
+ * If the file doesn't currently exist, then it will simply return the
+ * filepath.
+ */
+ function testFileDestination() {
+ // First test for non-existent file.
+ $destination = 'misc/xyz.txt';
+ $path = file_destination($destination, FILE_EXISTS_REPLACE);
+ $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_REPLACE.', 'File');
+ $path = file_destination($destination, FILE_EXISTS_RENAME);
+ $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_RENAME.', 'File');
+ $path = file_destination($destination, FILE_EXISTS_ERROR);
+ $this->assertEqual($path, $destination, 'Non-existing filepath destination is correct with FILE_EXISTS_ERROR.', 'File');
+
+ $destination = 'misc/druplicon.png';
+ $path = file_destination($destination, FILE_EXISTS_REPLACE);
+ $this->assertEqual($path, $destination, 'Existing filepath destination remains the same with FILE_EXISTS_REPLACE.', 'File');
+ $path = file_destination($destination, FILE_EXISTS_RENAME);
+ $this->assertNotEqual($path, $destination, 'A new filepath destination is created when filepath destination already exists with FILE_EXISTS_RENAME.', 'File');
+ $path = file_destination($destination, FILE_EXISTS_ERROR);
+ $this->assertEqual($path, FALSE, 'An error is returned when filepath destination already exists with FILE_EXISTS_ERROR.', 'File');
+ }
+
+ /**
+ * Ensure that the file_directory_temp() function always returns a value.
+ */
+ function testFileDirectoryTemp() {
+ // Start with an empty variable to ensure we have a clean slate.
+ variable_set('file_temporary_path', '');
+ $tmp_directory = file_directory_temp();
+ $this->assertEqual(empty($tmp_directory), FALSE, 'file_directory_temp() returned a non-empty value.');
+ $setting = variable_get('file_temporary_path', '');
+ $this->assertEqual($setting, $tmp_directory, "The 'file_temporary_path' variable has the same value that file_directory_temp() returned.");
+ }
+}
+
+/**
+ * Directory related tests.
+ */
+class RemoteFileDirectoryTest extends FileDirectoryTest {
+ public static function getInfo() {
+ $info = parent::getInfo();
+ $info['group'] = 'File API (remote)';
+ return $info;
+ }
+
+ function setUp() {
+ parent::setUp('file_test');
+ variable_set('file_default_scheme', 'dummy-remote');
+ }
+}
+
+/**
+ * Tests the file_scan_directory() function.
+ */
+class FileScanDirectoryTest extends FileTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'File scan directory',
+ 'description' => 'Tests the file_scan_directory() function.',
+ 'group' => 'File API',
+ );
+ }
+
+ function setUp() {
+ parent::setUp();
+ $this->path = drupal_get_path('module', 'simpletest') . '/files';
+ }
+
+ /**
+ * Check the format of the returned values.
+ */
+ function testReturn() {
+ // Grab a listing of all the JavaSscript files and check that they're
+ // passed to the callback.
+ $all_files = file_scan_directory($this->path, '/^javascript-/');
+ ksort($all_files);
+ $this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
+
+ // Check the first file.
+ $file = reset($all_files);
+ $this->assertEqual(key($all_files), $file->uri, 'Correct array key was used for the first returned file.');
+ $this->assertEqual($file->uri, $this->path . '/javascript-1.txt', 'First file name was set correctly.');
+ $this->assertEqual($file->filename, 'javascript-1.txt', 'First basename was set correctly');
+ $this->assertEqual($file->name, 'javascript-1', 'First name was set correctly.');
+
+ // Check the second file.
+ $file = next($all_files);
+ $this->assertEqual(key($all_files), $file->uri, 'Correct array key was used for the second returned file.');
+ $this->assertEqual($file->uri, $this->path . '/javascript-2.script', 'Second file name was set correctly.');
+ $this->assertEqual($file->filename, 'javascript-2.script', 'Second basename was set correctly');
+ $this->assertEqual($file->name, 'javascript-2', 'Second name was set correctly.');
+ }
+
+ /**
+ * Check that the callback function is called correctly.
+ */
+ function testOptionCallback() {
+ // When nothing is matched nothing should be passed to the callback.
+ $all_files = file_scan_directory($this->path, '/^NONEXISTINGFILENAME/', array('callback' => 'file_test_file_scan_callback'));
+ $this->assertEqual(0, count($all_files), 'No files were found.');
+ $results = file_test_file_scan_callback();
+ file_test_file_scan_callback_reset();
+ $this->assertEqual(0, count($results), 'No files were passed to the callback.');
+
+ // Grab a listing of all the JavaSscript files and check that they're
+ // passed to the callback.
+ $all_files = file_scan_directory($this->path, '/^javascript-/', array('callback' => 'file_test_file_scan_callback'));
+ $this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
+ $results = file_test_file_scan_callback();
+ file_test_file_scan_callback_reset();
+ $this->assertEqual(2, count($results), 'Files were passed to the callback.');
+ }
+
+ /**
+ * Check that the no-mask parameter is honored.
+ */
+ function testOptionNoMask() {
+ // Grab a listing of all the JavaSscript files.
+ $all_files = file_scan_directory($this->path, '/^javascript-/');
+ $this->assertEqual(2, count($all_files), 'Found two, expected javascript files.');
+
+ // Now use the nomast parameter to filter out the .script file.
+ $filtered_files = file_scan_directory($this->path, '/^javascript-/', array('nomask' => '/.script$/'));
+ $this->assertEqual(1, count($filtered_files), 'Filtered correctly.');
+ }
+
+ /**
+ * Check that key parameter sets the return value's key.
+ */
+ function testOptionKey() {
+ // "filename", for the path starting with $dir.
+ $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
+ $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filepath')));
+ sort($actual);
+ $this->assertEqual($expected, $actual, 'Returned the correct values for the filename key.');
+
+ // "basename", for the basename of the file.
+ $expected = array('javascript-1.txt', 'javascript-2.script');
+ $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'filename')));
+ sort($actual);
+ $this->assertEqual($expected, $actual, 'Returned the correct values for the basename key.');
+
+ // "name" for the name of the file without an extension.
+ $expected = array('javascript-1', 'javascript-2');
+ $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'name')));
+ sort($actual);
+ $this->assertEqual($expected, $actual, 'Returned the correct values for the name key.');
+
+ // Invalid option that should default back to "filename".
+ $expected = array($this->path . '/javascript-1.txt', $this->path . '/javascript-2.script');
+ $actual = array_keys(file_scan_directory($this->path, '/^javascript-/', array('key' => 'INVALID')));
+ sort($actual);
+ $this->assertEqual($expected, $actual, 'An invalid key defaulted back to the default.');
+ }
+
+ /**
+ * Check that the recurse option decends into subdirectories.
+ */
+ function testOptionRecurse() {
+ $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => FALSE));
+ $this->assertTrue(empty($files), "Without recursion couldn't find javascript files.");
+
+ $files = file_scan_directory(drupal_get_path('module', 'simpletest'), '/^javascript-/', array('recurse' => TRUE));
+ $this->assertEqual(2, count($files), 'With recursion we found the expected javascript files.');
+ }
+
+
+ /**
+ * Check that the min_depth options lets us ignore files in the starting
+ * directory.
+ */
+ function testOptionMinDepth() {
+ $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 0));
+ $this->assertEqual(2, count($files), 'No minimum-depth gets files in current directory.');
+
+ $files = file_scan_directory($this->path, '/^javascript-/', array('min_depth' => 1));
+ $this->assertTrue(empty($files), "Minimum-depth of 1 successfully excludes files from current directory.");
+ }
+}
+
+/**
+ * Tests the file_scan_directory() function on remote filesystems.
+ */
+class RemoteFileScanDirectoryTest extends FileScanDirectoryTest {
+ public static function getInfo() {
+ $info = parent::getInfo();
+ $info['group'] = 'File API (remote)';
+ return $info;
+ }
+
+ function setUp() {
+ parent::setUp('file_test');
+ variable_set('file_default_scheme', 'dummy-remote');
+ }
+}
+
+/**
+ * Deletion related tests.
+ */
+class FileUnmanagedDeleteTest extends FileTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Unmanaged file delete',
+ 'description' => 'Tests the unmanaged file delete function.',
+ 'group' => 'File API',
+ );
+ }
+
+ /**
+ * Delete a normal file.
+ */
+ function testNormal() {
+ // Create a file for testing
+ $file = $this->createFile();
+
+ // Delete a regular file
+ $this->assertTrue(file_unmanaged_delete($file->uri), 'Deleted worked.');
+ $this->assertFalse(file_exists($file->uri), 'Test file has actually been deleted.');
+ }
+
+ /**
+ * Try deleting a missing file.
+ */
+ function testMissing() {
+ // Try to delete a non-existing file
+ $this->assertTrue(file_unmanaged_delete(file_default_scheme() . '/' . $this->randomName()), 'Returns true when deleting a non-existent file.');
+ }
+
+ /**
+ * Try deleting a directory.
+ */
+ function testDirectory() {
+ // A directory to operate on.
+ $directory = $this->createDirectory();
+
+ // Try to delete a directory
+ $this->assertFalse(file_unmanaged_delete($directory), 'Could not delete the delete directory.');
+ $this->assertTrue(file_exists($directory), 'Directory has not been deleted.');
+ }
+}
+
+/**
+ * Deletion related tests on remote filesystems.
+ */
+class RemoteFileUnmanagedDeleteTest extends FileUnmanagedDeleteTest {
+ public static function getInfo() {
+ $info = parent::getInfo();
+ $info['group'] = 'File API (remote)';
+ return $info;
+ }
+
+ function setUp() {
+ parent::setUp('file_test');
+ variable_set('file_default_scheme', 'dummy-remote');
+ }
+}
+
+/**
+ * Deletion related tests.
+ */
+class FileUnmanagedDeleteRecursiveTest extends FileTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Unmanaged recursive file delete',
+ 'description' => 'Tests the unmanaged file delete recursive function.',
+ 'group' => 'File API',
+ );
+ }
+
+ /**
+ * Delete a normal file.
+ */
+ function testSingleFile() {
+ // Create a file for testing
+ $filepath = file_default_scheme() . '://' . $this->randomName();
+ file_put_contents($filepath, '');
+
+ // Delete the file.
+ $this->assertTrue(file_unmanaged_delete_recursive($filepath), 'Function reported success.');
+ $this->assertFalse(file_exists($filepath), 'Test file has been deleted.');
+ }
+
+ /**
+ * Try deleting an empty directory.
+ */
+ function testEmptyDirectory() {
+ // A directory to operate on.
+ $directory = $this->createDirectory();
+
+ // Delete the directory.
+ $this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
+ $this->assertFalse(file_exists($directory), 'Directory has been deleted.');
+ }
+
+ /**
+ * Try deleting a directory with some files.
+ */
+ function testDirectory() {
+ // A directory to operate on.
+ $directory = $this->createDirectory();
+ $filepathA = $directory . '/A';
+ $filepathB = $directory . '/B';
+ file_put_contents($filepathA, '');
+ file_put_contents($filepathB, '');
+
+ // Delete the directory.
+ $this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
+ $this->assertFalse(file_exists($filepathA), 'Test file A has been deleted.');
+ $this->assertFalse(file_exists($filepathB), 'Test file B has been deleted.');
+ $this->assertFalse(file_exists($directory), 'Directory has been deleted.');
+ }
+
+ /**
+ * Try deleting subdirectories with some files.
+ */
+ function testSubDirectory() {
+ // A directory to operate on.
+ $directory = $this->createDirectory();
+ $subdirectory = $this->createDirectory($directory . '/sub');
+ $filepathA = $directory . '/A';
+ $filepathB = $subdirectory . '/B';
+ file_put_contents($filepathA, '');
+ file_put_contents($filepathB, '');
+
+ // Delete the directory.
+ $this->assertTrue(file_unmanaged_delete_recursive($directory), 'Function reported success.');
+ $this->assertFalse(file_exists($filepathA), 'Test file A has been deleted.');
+ $this->assertFalse(file_exists($filepathB), 'Test file B has been deleted.');
+ $this->assertFalse(file_exists($subdirectory), 'Subdirectory has been deleted.');
+ $this->assertFalse(file_exists($directory), 'Directory has been deleted.');
+ }
+}
+
+/**
+ * Deletion related tests on remote filesystems.
+ */
+class RemoteFileUnmanagedDeleteRecursiveTest extends FileUnmanagedDeleteRecursiveTest {
+ public static function getInfo() {
+ $info = parent::getInfo();
+ $info['group'] = 'File API (remote)';
+ return $info;
+ }
+
+ function setUp() {
+ parent::setUp('file_test');
+ variable_set('file_default_scheme', 'dummy-remote');
+ }
+}
+
+/**
+ * Unmanaged move related tests.
+ */
+class FileUnmanagedMoveTest extends FileTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Unmanaged file moving',
+ 'description' => 'Tests the unmanaged file move function.',
+ 'group' => 'File API',
+ );
+ }
+
+ /**
+ * Move a normal file.
+ */
+ function testNormal() {
+ // Create a file for testing
+ $file = $this->createFile();
+
+ // Moving to a new name.
+ $desired_filepath = 'public://' . $this->randomName();
+ $new_filepath = file_unmanaged_move($file->uri, $desired_filepath, FILE_EXISTS_ERROR);
+ $this->assertTrue($new_filepath, 'Move was successful.');
+ $this->assertEqual($new_filepath, $desired_filepath, 'Returned expected filepath.');
+ $this->assertTrue(file_exists($new_filepath), 'File exists at the new location.');
+ $this->assertFalse(file_exists($file->uri), 'No file remains at the old location.');
+ $this->assertFilePermissions($new_filepath, variable_get('file_chmod_file', 0664));
+
+ // Moving with rename.
+ $desired_filepath = 'public://' . $this->randomName();
+ $this->assertTrue(file_exists($new_filepath), 'File exists before moving.');
+ $this->assertTrue(file_put_contents($desired_filepath, ' '), 'Created a file so a rename will have to happen.');
+ $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
+ $this->assertTrue($newer_filepath, 'Move was successful.');
+ $this->assertNotEqual($newer_filepath, $desired_filepath, 'Returned expected filepath.');
+ $this->assertTrue(file_exists($newer_filepath), 'File exists at the new location.');
+ $this->assertFalse(file_exists($new_filepath), 'No file remains at the old location.');
+ $this->assertFilePermissions($newer_filepath, variable_get('file_chmod_file', 0664));
+
+ // TODO: test moving to a directory (rather than full directory/file path)
+ // TODO: test creating and moving normal files (rather than streams)
+ }
+
+ /**
+ * Try to move a missing file.
+ */
+ function testMissing() {
+ // Move non-existent file.
+ $new_filepath = file_unmanaged_move($this->randomName(), $this->randomName());
+ $this->assertFalse($new_filepath, 'Moving a missing file fails.');
+ }
+
+ /**
+ * Try to move a file onto itself.
+ */
+ function testOverwriteSelf() {
+ // Create a file for testing.
+ $file = $this->createFile();
+
+ // Move the file onto itself without renaming shouldn't make changes.
+ $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_REPLACE);
+ $this->assertFalse($new_filepath, 'Moving onto itself without renaming fails.');
+ $this->assertTrue(file_exists($file->uri), 'File exists after moving onto itself.');
+
+ // Move the file onto itself with renaming will result in a new filename.
+ $new_filepath = file_unmanaged_move($file->uri, $file->uri, FILE_EXISTS_RENAME);
+ $this->assertTrue($new_filepath, 'Moving onto itself with renaming works.');
+ $this->assertFalse(file_exists($file->uri), 'Original file has been removed.');
+ $this->assertTrue(file_exists($new_filepath), 'File exists after moving onto itself.');
+ }
+}
+
+/**
+ * Unmanaged move related tests on remote filesystems.
+ */
+class RemoteFileUnmanagedMoveTest extends FileUnmanagedMoveTest {
+ public static function getInfo() {
+ $info = parent::getInfo();
+ $info['group'] = 'File API (remote)';
+ return $info;
+ }
+
+ function setUp() {
+ parent::setUp('file_test');
+ variable_set('file_default_scheme', 'dummy-remote');
+ }
+}
+
+/**
+ * Unmanaged copy related tests.
+ */
+class FileUnmanagedCopyTest extends FileTestCase {
+ public static function getInfo() {
+ return array(
+ 'name' => 'Unmanaged file copying',
+ 'description' => 'Tests the unmanaged file copy function.',
+ 'group' => 'File API',
+ );
+ }
+
+ /**
+ * Copy a normal file.
+ */
+ function testNormal() {
+ // Create a file for testing
+ $file = $this->createFile();
+
+ // Copying to a new name.
+ $desired_filepath = 'public://' . $this->randomName();
+ $new_filepath = file_unmanaged_copy($file->uri, $desired_filepath, FILE_EXISTS_ERROR);
+ $this->assertTrue($new_filepath, 'Copy was successful.');
+ $this->assertE