diff --git a/includes/mime_detect.inc b/includes/mime_detect.inc index 67566743..29fcf1e5 100644 --- a/includes/mime_detect.inc +++ b/includes/mime_detect.inc @@ -134,7 +134,7 @@ class MimeDetect { 'xhtml' => 'application/xhtml+xml', 'xsl' => 'text/xsl', 'xslt' => 'text/xsl', - 'xml' => 'text/xml', + 'xml' => 'application/xml', 'csv' => 'text/csv', 'tsv' => 'text/tab-separated-values', 'txt' => 'text/plain', @@ -223,7 +223,6 @@ class MimeDetect { "tar" => "application/x-tar", "gtar" => "application/x-gtar", "zip" => "application/x-zip", - "rng" => "application/xml", // others: 'bin' => 'application/octet-stream', // Web Archives: diff --git a/islandora.info b/islandora.info index e06d0de9..55c50359 100644 --- a/islandora.info +++ b/islandora.info @@ -13,7 +13,11 @@ files[] = includes/dublin_core.inc files[] = includes/tuque.inc files[] = includes/tuque_wrapper.inc files[] = includes/object.entity_controller.inc -files[] = tests/islandora_web_test_case.inc +files[] = tests/includes/datastream_validators.inc +files[] = tests/includes/islandora_web_test_case.inc +files[] = tests/includes/islandora_unit_test_case.inc +files[] = tests/includes/utilities.inc +files[] = tests/includes/test_utility_abstraction.inc files[] = tests/authtokens.test files[] = tests/hooks.test files[] = tests/ingest.test @@ -22,6 +26,5 @@ files[] = tests/islandora_manage_permissions.test files[] = tests/datastream_versions.test files[] = tests/datastream_cache.test files[] = tests/derivatives.test -files[] = tests/islandora_manage_temp_file.test files[] = tests/datastream_validator_tests.test php = 5.3 diff --git a/islandora.module b/islandora.module index 4691028e..3b10d64b 100644 --- a/islandora.module +++ b/islandora.module @@ -1814,15 +1814,12 @@ function islandora_form_simpletest_test_form_alter(array &$form) { function islandora_repair_drupal_filter() { // Grab the config. - $path = drupal_get_path('module', 'islandora'); - if (file_exists("$path/tests/test_config.ini")) { - $configuration = parse_ini_file("$path/tests/test_config.ini"); - } - elseif (file_exists("$path/tests/default.test_config.ini")) { - $configuration = parse_ini_file("$path/tests/default.test_config.ini"); + module_load_include('inc', 'islandora', 'tests/utilities/test_utility_abstraction'); + try { + $configuration = IslandoraTestUtilityClass::getTestConfiguration(); } - else { - drupal_set_message(t('Required default.test_config.ini/test_config.ini file not found'), 'error'); + catch (Exception $e) { + drupal_set_message(t("Error parsing test configuration: %e", array('%e' => $e)), 'error'); return FALSE; } diff --git a/tests/datastream_validator_tests.test b/tests/datastream_validator_tests.test index 79e1df7f..40615ae4 100644 --- a/tests/datastream_validator_tests.test +++ b/tests/datastream_validator_tests.test @@ -4,8 +4,6 @@ * Tests for things that test tests. Madness. */ -include 'datastream_validators.inc'; - /** * A test DatastreamValidator for the DatastreamValidatorResultTestCase. */ @@ -62,7 +60,7 @@ class DatastreamValidatorResultTestCase extends IslandoraWebTestCase { * Generates a generic DatastreamValidatorResult and grabs its properties. */ public function testDatastreamValidatorResult() { - $result = new DatastreamValidatorResult(TRUE, 'true', array()); + $result = new IslandoraTestUtilityResult(TRUE, 'true', array()); $this->assertEqual($result->getMessage(), 'true', "Result message generated correctly.", 'Islandora'); $this->assertEqual($result->getType(), TRUE, "Result type generated correctly.", 'Islandora'); $this->assertEqual($result->getCaller(), array(), "Result caller generated correctly.", 'Islandora'); @@ -111,10 +109,11 @@ class DatastreamValidatorResultTestCase extends IslandoraWebTestCase { $this->assertTrue(substr($first_caller['file'], -48) === substr($second_caller['file'], -48), "Fail caller file matches the pass caller file.", 'Islandora'); $this->assertTrue($first_caller['function'] === 'TestDatastreamValidator->assertSomethingSuccessfully()', "Correct pass caller function returned (actual: {$first_caller['function']}; expected: TestDatastreamValidator->assertSomethingSuccessfully()).", 'Islandora'); $this->assertTrue($second_caller['function'] === 'TestDatastreamValidator->assertSomethingFailed()', "Correct fail caller function returned (actual: {$second_caller['function']}; expected: TestDatastreamValidator->assertSomethingFailed()).", 'Islandora'); - $this->assertTrue($first_caller['line'] === 18, "Correct pass line number returned (actual: {$first_caller['line']}; expected: 9).", 'Islandora'); - $this->assertTrue($second_caller['line'] === 25, "Correct fail line number returned (actual: {$second_caller['line']}; expected: 13).", 'Islandora'); + $this->assertTrue($first_caller['line'] === 16, "Correct pass line number returned (actual: {$first_caller['line']}; expected: 16).", 'Islandora'); + $this->assertTrue($second_caller['line'] === 23, "Correct fail line number returned (actual: {$second_caller['line']}; expected: 23).", 'Islandora'); } } + } /** diff --git a/tests/datastream_validators.inc b/tests/includes/datastream_validators.inc similarity index 91% rename from tests/datastream_validators.inc rename to tests/includes/datastream_validators.inc index 151ec676..b8ff785e 100644 --- a/tests/datastream_validators.inc +++ b/tests/includes/datastream_validators.inc @@ -28,7 +28,7 @@ function islandora_hex2int($hex) { drupal_set_message(t('String passed to islandora_hex2int() contains non-hexidecimal characters.'), 'error'); return FALSE; } - if (!strlen($hex) === 4 || !strlen($hex) === 8) { + if (!(strlen($hex) == 4 || strlen($hex) == 8)) { drupal_set_message(t('String passed to islandora_hex2int() cannot create a 16- or 32-bit little-endian signed integer'), 'error'); return FALSE; } @@ -44,84 +44,11 @@ function islandora_hex2int($hex) { } } -/** - * A result from a datastream validator; $type defines TRUE/FALSE as pass/fail. - */ -class DatastreamValidatorResult { - - /** - * The message for this result. - * - * @var string - */ - protected $message; - - /** - * The caller for this result. - * - * @var array - */ - protected $caller; - - /** - * The type of result this is - TRUE for pass, FALSE for fail. - * - * @var bool - */ - protected $type; - - /** - * Constructs a DatastreamValidatorResult. - * - * @param bool $type - * Whether this result should indicate a pass (TRUE) or fail (FALSE). - * @param string $message - * The message that will be used by this result. - * @param array $caller - * The caller for this result. - */ - public function __construct($type, $message, array $caller) { - $this->message = $message; - $this->caller = $caller; - $this->type = $type; - } - - /** - * Get the message for this result. - * - * @return string - * The message for this result. - */ - public function getMessage() { - return $this->message; - } - - /** - * Get the caller for this result. - * - * @return string - * The caller for this result. - */ - public function getCaller() { - return $this->caller; - } - - /** - * Get the type of result. - * - * @return bool - * The type of pass (TRUE for pass, FALSE for fail). - */ - public function getType() { - return $this->type; - } -} - /** * Abstraction for datastream validators. * * Classes extended from DatastreamValidator don't require much to be useful. - * They accept an IslandoraFedoraObject and a DSID to perform assertions on; + * They accept a Fedora object and a DSID to perform assertions on; * all you have to do is place a series of functions inside the extended class * using the naming convention assertThing(); each of these functions should * ideally assert one thing and one thing only (for simplicity's sake), and @@ -137,12 +64,12 @@ class DatastreamValidatorResult { * all the test results using getPasses() and getFails() and transforms those * into something that DrupalWebTestCase can use. */ -abstract class DatastreamValidator { +abstract class DatastreamValidator extends IslandoraTestUtilityClass { /** - * The IslandoraFedoraObject containing the datastream to test. + * The Fedora object containing the datastream to test. * - * @var IslandoraFedoraObject + * @var IslandoraFedoraObject|FedoraObject */ public $object; @@ -160,15 +87,6 @@ abstract class DatastreamValidator { */ public $datastreamContent; - /** - * An array of DatastreamValidatorResults. - * - * These should be generated using $this->addResult. - * - * @var DatastreamValidatorResult[] - */ - public $results = array(); - /** * An array of additional required parameters. * @@ -179,14 +97,14 @@ abstract class DatastreamValidator { /** * Constructs a DatastreamValidator. * - * @param IslandoraFedoraObject $object + * @param IslandoraFedoraObject|FedoraObject $object * The object to grab the datastream from. * @param string $datastream * The DSID of the datastream itself. * @param array $params * An extra array of parameters the validator might need. */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + public function __construct($object, $datastream, array $params = array()) { $this->object = $object; $this->datastream = $datastream; $this->params = $params; @@ -210,18 +128,23 @@ abstract class DatastreamValidator { * fails to $this->passes and/or $this->fails. */ public function runValidators() { - $methods = get_class_methods($this); - foreach ($methods as $method) { - if (substr($method, 0, 6) === 'assert') { - $this->$method(); + if ($this->object[$this->datastream]) { + $methods = get_class_methods($this); + foreach ($methods as $method) { + if (substr($method, 0, 6) === 'assert') { + $this->$method(); + } } } + else { + $this->addResult(FALSE, "Unable to load the requested datastream {$this->datastream} from object {$this->object->id}."); + } } /** - * Returns an array of DatastreamValidatorResults. + * Returns an array of IslandoraTestUtilityResults. * - * @return DatastreamValidatorResult[] + * @return IslandoraTestUtilityResult[] * The results. */ public function getResults() { @@ -240,7 +163,7 @@ abstract class DatastreamValidator { * The message to put in the result. */ public function addResult($type, $message) { - $result = new DatastreamValidatorResult($type, $message, $this->getAssertionCall()); + $result = new IslandoraTestUtilityResult($type, $message, $this->getAssertionCall()); $this->results[] = $result; } @@ -254,7 +177,7 @@ abstract class DatastreamValidator { * @return array * Array representing the true caller. */ - protected function getAssertionCall() { + public function getAssertionCall() { $backtrace = debug_backtrace(); // While the current caller's function starts with 'assert', and another one @@ -420,7 +343,7 @@ class TextDatastreamValidator extends DatastreamValidator { /** * Constructor override; blow up if we don't have our two values. */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + public function __construct($object, $datastream, array $params = array()) { if (count($params) < 2) { throw new InvalidArgumentException('$params must contain at least two values to instantiate a TextDatastreamValidator.'); } @@ -463,14 +386,14 @@ class WAVDatastreamValidator extends DatastreamValidator { /** * We need a special constructor here to get the hex datastream content. * - * @param IslandoraFedoraObject $object + * @param IslandoraFedoraObject|FedoraObject $object * The object to grab the datastream from. * @param string $datastream * The DSID of the datastream itself. * @param array $params * An extra array of parameters the validator might need. */ - public function __construct(IslandoraFedoraObject $object, $datastream, array $params = array()) { + public function __construct($object, $datastream, array $params = array()) { parent::__construct($object, $datastream, $params); $this->datastreamContent = bin2hex($this->datastreamContent); } diff --git a/tests/includes/islandora_unit_test_case.inc b/tests/includes/islandora_unit_test_case.inc new file mode 100644 index 00000000..da0a8a71 --- /dev/null +++ b/tests/includes/islandora_unit_test_case.inc @@ -0,0 +1,76 @@ + FALSE); + $utilities = new IslandoraTestUtilities($this->configuration, $params); + if (!method_exists($utilities, $method)) { + $caller = $this->getAssertionCall(); + throw new BadMethodCallException("Exception: undefined method $method in {$caller['file']}, line {$caller['line']}."); + } + $result = call_user_func_array(array(&$utilities, $method), $args); + $this->parseUtilityResults($utilities); + return $result; + } + + /** + * Parses utility results and passes them to the test results as an assertion. + * + * @param IslandoraTestUtilities $utility + * An instance of IslandoraTestUtilities with populated results. + */ + public function parseUtilityResults($utility) { + foreach ($utility->getResults() as $result) { + $this->assert($result->getType(), $result->getMessage(), 'Islandora', $result->getCaller()); + } + } + + /** + * Sets up the Drupal filter to access this test Drupal instances database. + * + * @see DrupalWebTestCase::setUp() + */ + public function setUp() { + parent::setUp(); + + // It's possible test are running before class autoloading. + module_load_include('inc', 'islandora', 'includes/tuque'); + + $this->configuration = IslandoraTestUtilityClass::getTestConfiguration(); + $this->connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); + $api = new FedoraApi($this->connection); + $this->repository = new FedoraRepository($api, new SimpleCache()); + } + + /** + * Frees any allocated resources. + * + * @see DrupalWebTestCase::tearDown() + */ + public function tearDown() { + unset($this->configuration); + parent::tearDown(); + } + +} diff --git a/tests/includes/islandora_web_test_case.inc b/tests/includes/islandora_web_test_case.inc new file mode 100644 index 00000000..55ee5824 --- /dev/null +++ b/tests/includes/islandora_web_test_case.inc @@ -0,0 +1,332 @@ + $this->loggedInUser, + 'db_access' => TRUE, + ); + $utilities = new IslandoraTestUtilities($this->configuration, $params); + if (!method_exists($utilities, $method)) { + $caller = $this->getAssertionCall(); + throw new BadMethodCallException("Exception: undefined method $method in {$caller['file']}, line {$caller['line']}."); + } + $result = call_user_func_array(array(&$utilities, $method), $args); + $this->parseUtilityResults($utilities); + return $result; + } + + /** + * Parses utility results and passes them to the test results as an assertion. + * + * @param IslandoraTestUtilities $utility + * An instance of IslandoraTestUtilities with populated results. + */ + public function parseUtilityResults($utility) { + foreach ($utility->getResults() as $result) { + $this->assert($result->getType(), $result->getMessage(), 'Islandora', $result->getCaller()); + } + } + + /** + * Sets up the web test case. + * + * @see DrupalWebTestCase::setUp() + */ + public function setUp() { + $args = func_get_args(); + $args = (isset($args[0]) && is_array($args[0])) ? $args[0] : $args; + // Always enable islandora. + $args[] = 'islandora'; + parent::setUp($args); + + // It's possible test are running before class autoloading. + module_load_include('inc', 'islandora', 'includes/tuque'); + module_load_include('inc', 'islandora', 'includes/tuque_wrapper'); + module_load_include('inc', 'islandora', 'tests/includes/utilities'); + + $this->configuration = IslandoraTestUtilityClass::getTestConfiguration(); + if ($this->configuration['use_drupal_filter']) { + $this->setUpDrupalFilter(); + } + $this->admin = $this->createAdminUser(); + } + + /** + * Creates the a full fedora admin user with a repository connection. + */ + protected function createAdminUser() { + $roles = user_roles(); + $index = array_search('administrator', $roles); + $user = $this->drupalCreateUser(); + $user->roles[$index] = 'administrator'; + $user->name = $this->configuration['admin_user']; + $user->pass = $this->configuration['admin_pass']; + $user = user_save($user); + $url = variable_get('islandora_base_url', $this->configuration['fedora_url']); + $connection = islandora_get_tuque_connection($user, $url); + $user->repository = $connection->repository; + return $user; + } + + /** + * Logs in the given user, handles the special case where the user is admin. + * + * @see DrupalWebTestCase::drupalLogin() + */ + protected function drupalLogin(stdClass $account) { + if ($account->uid == $this->admin->uid) { + // Create password for Drupal. + $edit = array('pass' => user_password()); + $account = user_save($account, $edit); + // Raw password is used to login. + $account->pass_raw = $edit['pass']; + // We must login before changing the password for fedora. + parent::drupalLogin($account); + $account->name = $this->configuration['admin_user']; + $account->pass = $this->configuration['admin_pass']; + // Save the fedora admin credentials for later GET/POST requests. + $account = user_save($account); + } + else { + parent::drupalLogin($account); + $this->users[] = $account->name; + } + } + + /** + * Restores the original Drupal filter, frees any allocated resources. + * + * To safeguard against leaving test objects in the repository, tearDown() + * calls deleteUserCreatedObjects() every time by default. This feature can be + * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. + * + * @see DrupalWebTestCase::tearDown() + */ + public function tearDown() { + if ($this->deleteObjectsOnTeardown) { + foreach ($this->users as $user) { + $this->deleteUserCreatedObjects($user); + } + } + if ($this->configuration['use_drupal_filter']) { + islandora_repair_drupal_filter(); + } + unset($this->admin); + unset($this->configuration); + parent::tearDown(); + } + + /** + * Gets a tuque object from a path. + * + * @param string $path + * A full or partial path to an islandora object. + * + * @return AbstractObject + * The pid of the object or FALSE if a PID is not found. + */ + public function getObjectFromPath($path) { + $path_parts = explode('/', $path); + $array_length = count($path_parts); + for ($i = 0; $i < $array_length; $i++) { + if ($path_parts[$i] == 'islandora' && isset($path_parts[$i + 1]) && $path_parts[$i + 1] == 'object') { + if (isset($path_parts[$i + 2])) { + return islandora_object_load(urldecode($path_parts[$i + 2])); + } + } + } + $this->fail("Failed to parse path: $path."); + return FALSE; + } + + /** + * Deletes an object using the PID. This does the deletion using the UI. + * + * @param string $pid + * The PID of the collection to be deleted + * @param string $button + * The label of the first 'Delete' button + * @param bool $safety + * If TRUE, this will only delete objects owned by users in $this->users. + */ + public function deleteObject($pid, $button = NULL, $safety = TRUE) { + $object = islandora_object_load($pid); + if (!$safety || in_array($object->owner, $this->users)) { + $path = "islandora/object/$pid/manage/properties"; + if (isset($button)) { + $this->drupalPost($path, array(), $button); + } + else { + $object = islandora_object_load($pid); + $this->drupalPost($path, array(), "Permanently remove '{$object->label}' from repository"); + } + $this->drupalPost($this->url, array(), t('Delete')); + + $this->drupalGet("islandora/object/$pid"); + $this->assertResponse(404, "Object $pid successfully deleted."); + } + else { + $this->fail("Cannot delete object {$pid}; it is owned by non-test user {$object->owner}, and this function was called with the safety on."); + return FALSE; + } + } + + /** + * These are a few quick helper functions to fill in a gap in the standard + * DrupalWebTestCase where no function exists to test for the simple existence + * or non-existence of an error. + */ + + /** + * Asserts that an error is found in $this->content. + * + * @param string $message + * The message to pass on to the results. + * @param string $group + * The group to place the result in. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function assertError($message = '', $group = 'Other') { + if (!$message) { + $message = "Error found on current page when error was expected."; + } + return $this->assertFieldByXPath('//div[contains(@class, "message") and contains(@class, "error")]', NULL, $message, $group); + } + + /** + * Asserts that no error is found in $this->content. + * + * @param string $message + * The message to pass on to the results. + * @param string $group + * The group to place the result in. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function assertNoError($message = '', $group = 'Other') { + if (!$message) { + $message = "No error found on current page when no error was expected."; + } + return $this->assertNoFieldByXPath('//div[contains(@class, "message") and contains(@class, "error")]', NULL, $message, $group); + } + + /** + * Asserts that a warning is found in $this->content. + * + * @param string $message + * The message to pass on to the results. + * @param string $group + * The group to place the result in. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function assertWarning($message = '', $group = 'Other') { + if (!$message) { + $message = "Warning found on current page when warning was expected."; + } + return $this->assertFieldByXPath('//div[contains(@class, "message") and contains(@class, "warning")]', NULL, $message, $group); + } + + /** + * Asserts that no warning is found in $this->content. + * + * @param string $message + * The message to pass on to the results. + * @param string $group + * The group to place the result in. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function assertNoWarning($message = '', $group = 'Other') { + if (!$message) { + $message = "No warning found on current page when no warning was expected."; + } + return $this->assertNoFieldByXPath('//div[contains(@class, "message") and contains(@class, "warning")]', NULL, $message, $group); + } + + /** + * Makes a drupalPost() request, using the form submit button's ID. + * + * Because drupalPost() is silly and doesn't let us choose which button we'd + * like to select if multiple buttons have the same value, this function + * allows us to select whatever button we'd like based on the ID instead. + * + * This is done via the absolutely hilarious method of fudging the actual + * button labels that don't have the ID we want, so that the only one left + * with the correct label is the one with the right ID. + * + * @see DrupalWebTestCase::drupalPost() + * + * @param string $path + * Location of the post form. + * @param array $edit + * Field data in an associative array. + * @param string $submit + * Value of the submit button whose click is to be emulated. + * @param string $id + * ID of the submit button whose click is to be emulated. + * @param array $options + * Options to be forwarded to url(). + * @param array $headers + * An array containing additional HTTP request headers, each formatted as + * "name: value". + * @param null $form_html_id + * (optional) HTML ID of the form to be submitted. + * @param null $extra_post + * (optional) A string of additional data to append to the POST submission. + * + * @return bool|string + * The content from the POST request's curlExec, or FALSE on fail. + */ + public function drupalPostByID($path, $edit, $submit, $id, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { + $buttons = $this->xpath("//input[@type=\"submit\" and @value=\"{$submit}\"]"); + if (empty($buttons)) { + $this->fail("No buttons found on the page with value '$submit'"); + return FALSE; + } + $i = 0; + foreach ($buttons as $button) { + if ($button['id'] !== $id) { + $button['value'] .= "_$i"; + $i++; + } + } + return $this->drupalPost($path, $edit, $submit, $options, $headers, $form_html_id, $extra_post); + } + +} diff --git a/tests/includes/test_utility_abstraction.inc b/tests/includes/test_utility_abstraction.inc new file mode 100644 index 00000000..aa09004b --- /dev/null +++ b/tests/includes/test_utility_abstraction.inc @@ -0,0 +1,153 @@ +message = $message; + $this->caller = $caller; + $this->type = $type; + } + + /** + * Get the message for this result. + * + * @return string + * The message for this result. + */ + public function getMessage() { + return $this->message; + } + + /** + * Get the caller for this result. + * + * @return string + * The caller for this result. + */ + public function getCaller() { + return $this->caller; + } + + /** + * Get the type of result. + * + * @return bool + * The type of pass (TRUE for pass, FALSE for fail). + */ + public function getType() { + return $this->type; + } +} + +/** + * Abstraction for test utility classes. + * + * This is to be implemented in any class that wants to have test utility + * functionality (i.e. that wants to pass back results to tests). Check out the + * datastream validator class for a good example of how this is implemented. + * + * Test utility classes should store per-installation configuration options in + * a test_config.ini file, contained in the islandora/tests folder. A function + * is included with the abstraction to parse the configuration file. + */ +abstract class IslandoraTestUtilityClass { + + /** + * An array of IslandoraTestUtilityResults. + * + * These should be generated using $this->addResult. + * + * @var IslandoraTestUtilityResult[] + */ + public $results = array(); + + /** + * Parses and returns the settings from the test configuration file. + * + * If no install specific test_config.ini file is found, it will use the + * assumed default configs found in default.test_config.ini. + * + * @return array + * The test configuration. + * + * @see parse_ini_file() + */ + public static function getTestConfiguration() { + $path = drupal_get_path('module', 'islandora'); + if (file_exists("$path/tests/test_config.ini")) { + return parse_ini_file("$path/tests/test_config.ini"); + } + elseif (file_exists("$path/tests/default.test_config.ini")) { + return parse_ini_file("$path/tests/default.test_config.ini"); + } + throw new Exception('Required default.test_config.ini/test_config.ini file not found'); + } + + /** + * Returns an array of IslandoraTestUtilityResults. + * + * The particular testing implementation you are using should use this to + * parse results from utilities and pass them through. + * + * @return IslandoraTestUtilityResult[] + * The results. + */ + abstract public function getResults(); + + /** + * Adds a result to $this->results. + * + * @param bool $type + * The type of result (TRUE for pass, FALSE for fail). + * @param string $message + * The message to put in the result. + */ + abstract public function addResult($type, $message); + + /** + * Gets the caller of the method that passed a result. + * + * @return array + * Array representing the true caller. + */ + abstract public function getAssertionCall(); + +} diff --git a/tests/includes/utilities.inc b/tests/includes/utilities.inc new file mode 100644 index 00000000..078e60d6 --- /dev/null +++ b/tests/includes/utilities.inc @@ -0,0 +1,354 @@ +configuration = $configuration; + $this->params = $params; + $connection = new RepositoryConnection($this->configuration['fedora_url'], $this->configuration['admin_user'], $this->configuration['admin_pass']); + $api = new FedoraApi($connection); + $this->repository = new FedoraRepository($api, new SimpleCache()); + } + + /** + * Sets up a drupal filter that can read from the tests users table. + */ + public function setUpDrupalFilter() { + $original_drupal_filter_content = file_get_contents($this->configuration['drupal_filter_file']); + $connection_info = Database::getConnectionInfo('default'); + $drupal_filter_dom = new DomDocument(); + $drupal_filter_dom->loadXML($original_drupal_filter_content); + $prefix = $connection_info['default']['prefix']['default']; + $filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0); + $first_connection_node = $drupal_filter_dom->getElementsByTagName('connection')->item(0); + $connection_node = $filter_drupal_connection_node->insertBefore($drupal_filter_dom->createElement('connection'), $first_connection_node); + $connection_node->setAttribute('server', $connection_info['default']['host']); + $connection_node->setAttribute('dbname', $connection_info['default']['database']); + $connection_node->setAttribute('user', $connection_info['default']['username']); + $connection_node->setAttribute('password', $connection_info['default']['password']); + $connection_node->setAttribute('port', $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'); + $sql_node = $drupal_filter_dom->createElement('sql', "SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;"); + $connection_node->appendChild($sql_node); + file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); + } + + /** + * Returns an array of IslandoraTestUtilityResults. + * + * @return IslandoraTestUtilityResult[] + * The results. + */ + public function getResults() { + return $this->results; + } + + /** + * Adds a result to $this->results. + * + * @param bool $type + * The type of result (TRUE for pass, FALSE for fail). + * @param string $message + * The message to put in the result. + */ + public function addResult($type, $message) { + $result = new IslandoraTestUtilityResult($type, $message, $this->getAssertionCall()); + $this->results[] = $result; + } + + /** + * Cycles through backtrace until the first non-assertion method is found. + * + * This is a manipulated version of DrupalWebTestCase::getAssertionCall(). + * We use it here so that we can pass back assertion calls from + * DatastreamValidator assertions instead of less useful TestCase functions. + * + * @return array + * Array representing the true caller. + */ + public function getAssertionCall() { + $backtrace = debug_backtrace(); + array_shift($backtrace); + return _drupal_get_last_caller($backtrace); + } + + /** + * Asserts that the given datastreams exist correctly on the object. + * + * @param AbstractObject $object + * The object to check. + * @param array $datastreams + * An array of strings containing datastream names. + * + * @return bool + * TRUE on success, FALSE on fail. + */ + public function assertDatastreams($object, array $datastreams) { + if (!self::assertFedoraObject($object)) { + $this->addResult(FALSE, "Failed. Object passed in is invalid.", 'Islandora'); + } + else { + $missing_datastreams = array_diff_key(array_flip($datastreams), $object->repository->api->a->listDatastreams($object->id)); + + if (!empty($missing_datastreams)) { + $this->addResult(FALSE, "Failed to find datastream(s) " . implode(', ', array_flip($missing_datastreams)) . " in object {$object->id}."); + return FALSE; + } + + $this->addResult(TRUE, "Found required datastream(s) in object {$object->id}"); + return TRUE; + } + } + + /** + * Asserts that the given datastreams do not exist on the object. + * + * @param AbstractObject $object + * The object to check. + * @param array $datastreams + * An array of datastreams to confirm not present. + */ + public function assertNoDatastreams($object, array $datastreams) { + if (!self::assertFedoraObject($object)) { + $this->addResult(FALSE, "Failed. Object passed in is invalid.", 'Islandora'); + return; + } + $found_datastreams = array_intersect_key(array_flip($datastreams), $object->repository->api->a->listDatastreams($object->id)); + + if (!empty($found_datastreams)) { + $this->addResult(FALSE, "Found unwanted datastream(s)" . implode(', ', array_flip($found_datastreams)) . " in object {$object->id}."); + return FALSE; + } + + $this->addResult(TRUE, "Unwanted datastream(s) not found in object {$object->id}"); + return TRUE; + } + + /** + * Attempts to validate an array of datastreams, generally via binary checks. + * + * Datastream validation classes exist in, and can be added to, the file + * 'datastream_validators.inc', which is found in this folder. Datastream + * validator classes use the naming convention 'PrefixDatastreamValidator', + * and that 'Prefix' is what this function uses to determine what class to + * instantiate. + * + * $param IslandoraFedoraObject $object + * The object to load datastreams from. + * $param array $datastreams + * An array of arrays that pair DSIDs, DatastreamValidator class prefixes, + * and optional params. You can check some of the existing implementations + * for examples. + */ + public function validateDatastreams($object, array $datastreams) { + + if (!self::assertFedoraObject($object)) { + $this->addResult(FALSE, "Datastream validation failed; Object passed in is invalid.", 'Islandora'); + return; + } + + module_load_include('inc', 'islandora', 'tests/includes/datastream_validators'); + + foreach ($datastreams as $datastream) { + // XXX: The "+ array (2 => array())" is to allow the value to be optional. + list($dsid, $prefix, $params) = $datastream + array(2 => array()); + + // Instantiate the appropriate class, and grab the results. + $class_name = "{$prefix}DatastreamValidator"; + if (class_exists($class_name)) { + $validator = new $class_name($object, $dsid, $params); + foreach ($validator->getResults() as $result) { + $this->addResult($result->getType(), $result->getMessage()); + } + } + else { + $this->addResult(FALSE, "No DatastreamValidator class was found with the name '$class_name'; are you sure the prefix given to IslandoraWebTestCase->validateDatastreams() was entered correctly, or that such a validator exists?", 'Islandora'); + } + } + } + + /** + * Constructs and ingests a Fedora object and datastream(s) via tuque. + * + * All keys inside the parameter arrays for this function are optional. it + * can be run simply by calling the method with no arguments. + * + * If your test case supports logged in Drupal users, IslandoraTestUtilities + * can be instantiated with $params['logged_in_user'] as that user object, and + * this method will set the owner of the ingested object as that user by + * default. + * + * @param array $properties + * An array containing object information using these keys: + * 'label' - The object label; randomized if not set. + * 'pid' - 'namespace:pid', or just 'namespace' to generate the suffix. + * 'models' - An array that can contain multiple content model PIDs, or a + * string containing a single content model PID. + * 'owner' - The object's owner. Defaults to the currently logged-in user, + * if available. It is recommended to set this to a value that can be found + * in $this->users; otherwise, this object will have to be manually deleted. + * 'parent' - The PID of the parent collection. + * @param array $datastreams + * An array containing zero or more datastream arrays that use the keys: + * 'dsid' - the datastream ID; randomized if not set. + * 'path' - The path to the file to use; defaults to fixtures/test.jpg. + * 'control_group' - The single-letter control group identifier. + * 'mimetype' - The datastream's mimetype. + * + * @return bool|array + * FALSE if the object ingest failed, or the object array if successful. + */ + public function ingestConstructedObject(array $properties = array(), array $datastreams = array()) { + if (!isset($properties['pid'])) { + $properties['pid'] = "islandora"; + } + $object = $this->repository->constructObject($properties['pid']); + + // Set the object properties before ingesting it. + if (isset($properties['label'])) { + $object->label = $properties['label']; + } + else { + $properties['label'] = DrupalUnitTestCase::randomName(); + $object->label = $properties['label']; + } + + if (isset($properties['owner'])) { + $object->owner = $properties['owner']; + } + elseif (isset($this->params['logged_in_user'])) { + $object->owner = $this->params['logged_in_user']->name; + } + + if (isset($properties['models'])) { + try { + $object->models = (array) $properties['models']; + } + catch (Exception $e) { + $this->addResult(FALSE, "Encountered an exception when trying to add content models to {$object->id}: $e"); + return FALSE; + } + } + + // Chuck in some datastreams. + if (!empty($datastreams)) { + foreach ($datastreams as $datastream) { + if (!isset($datastream['dsid'])) { + $datastream['dsid'] = DrupalUnitTestCase::randomName(); + } + if (!isset($datastream['path'])) { + $datastream['path'] = drupal_get_path('module', 'islandora') . '/tests/fixtures/test.jpg'; + } + if (!isset($datastream['control_group'])) { + $new_datastream = $object->constructDatastream($datastream['dsid']); + } + else { + $new_datastream = $object->constructDatastream($datastream['dsid'], $datastream['control_group']); + } + $new_datastream->label = $datastream['dsid']; + if (isset($datastream['mimetype'])) { + $new_datastream->mimetype = $datastream['mimetype']; + } + $new_datastream->setContentFromFile($datastream['path']); + $object->ingestDatastream($new_datastream); + } + } + + $this->repository->ingestObject($object); + if (!$object) { + $this->addResult(FALSE, t("Failed to ingest object."), 'Islandora'); + return FALSE; + } + else { + $this->addResult(TRUE, t("Ingested object %object", array('%object' => $object->id)), 'Islandora'); + } + + // Add a parent relationship, if necessary. + if (isset($properties['parent'])) { + $object->relationships->add(FEDORA_RELS_EXT_URI, 'isMemberOfCollection', $properties['parent']); + } + + return $object; + } + + /** + * Deletes all objects created by the given user. + * + * To safeguard against leaving test objects in the repository, this is called + * each time DrupalTestCase::run() calls tearDown(). This feature can be + * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. + * + * @param object $username + * The user whose objects we'd like to remove. + * + * @return bool + * TRUE on success, FALSE on failure. + */ + public function deleteUserCreatedObjects($username) { + if ($username === $this->configuration['admin_user']) { + $this->addResult(FALSE, "This function will under no circumstance attempt deletion of all objects owned by the configured Fedora admin user ({$this->configuration['admin_user']}), as this could irreparably damage the repository.", 'Islandora'); + return FALSE; + } + + $query = << WHERE +{ + ?object "$username" +} +QUERY; + + $objects = $this->repository->ri->sparqlQuery($query); + foreach ($objects as $object) { + $loaded_object = islandora_object_load($object['object']['value']); + $this->repository->api->m->purgeObject($loaded_object->id); + if (islandora_object_load($object['object']['value'])) { + $this->addResult(TRUE, "Object {$object['object']['value']} successfully removed from repository."); + return TRUE; + } + $this->addResult(FALSE, "Unable to remove object {$object['object']['value']} from the repository."); + return FALSE; + } + } + + /** + * Asserts that an object is a FedoraObject or an IslandoraFedoraObject. + * + * @param object $object + * The object to assess. + * + * @return bool + * TRUE if it is either of those object types, or FALSE otherwise. + */ + public static function assertFedoraObject($object) { + return ($object instanceof FedoraObject); + } + +} diff --git a/tests/islandora_web_test_case.inc b/tests/islandora_web_test_case.inc deleted file mode 100644 index 8a1e5ce8..00000000 --- a/tests/islandora_web_test_case.inc +++ /dev/null @@ -1,604 +0,0 @@ -configuration = $this->getTestConfiguration(); - if ($this->configuration['use_drupal_filter']) { - $this->backUpDrupalFilter(); - $this->setUpDrupalFilter(); - } - $this->admin = $this->createAdminUser(); - } - - /** - * Parses and returns the settings from the test configuration file. - * - * If no install specific test_config.ini file is found, it will use the - * assumed default configs found in default.test_config.ini. - * - * @return array - * The test configuration. - * - * @see parse_ini_file() - */ - protected function getTestConfiguration() { - $path = drupal_get_path('module', 'islandora'); - if (file_exists("$path/tests/test_config.ini")) { - $this->pass('Using custom test configuration.'); - return parse_ini_file("$path/tests/test_config.ini"); - } - elseif (file_exists("$path/tests/default.test_config.ini")) { - $this->pass('Using default test configuration.'); - return parse_ini_file("$path/tests/default.test_config.ini"); - } - throw new Exception('Required default.test_config.ini/test_config.ini file not found'); - } - - /** - * Stores the content of the Drupal Filter for later restoration. - */ - protected function backUpDrupalFilter() { - if (file_exists($this->configuration['drupal_filter_file'])) { - $this->originalDrupalFilterContent = file_get_contents($this->configuration['drupal_filter_file']); - } - else { - throw new Exception('Failed to find the required Drupal Filter configuration file.'); - } - } - - /** - * Sets up a drupal filter that can read for the tests users table. - */ - protected function setUpDrupalFilter() { - $connection_info = Database::getConnectionInfo('default'); - $drupal_filter_dom = new DomDocument(); - $drupal_filter_dom->loadXML($this->originalDrupalFilterContent); - $server = $connection_info['default']['host']; - $dbname = $connection_info['default']['database']; - $user = $connection_info['default']['username']; - $password = $connection_info['default']['password']; - $port = $connection_info['default']['port'] ? $connection_info['default']['port'] : '3306'; - $prefix = $connection_info['default']['prefix']['default']; - $filter_drupal_connection_node = $drupal_filter_dom->getElementsByTagName('FilterDrupal_Connection')->item(0); - $first_connection_node = $drupal_filter_dom->getElementsByTagName('connection')->item(0); - $connection_node = $filter_drupal_connection_node->insertBefore($drupal_filter_dom->createElement('connection'), $first_connection_node); - $connection_node->setAttributeNode(new DOMAttr('server', $server)); - $connection_node->setAttributeNode(new DOMAttr('dbname', $dbname)); - $connection_node->setAttributeNode(new DOMAttr('user', $user)); - $connection_node->setAttributeNode(new DOMAttr('password', $password)); - $connection_node->setAttributeNode(new DOMAttr('port', $port)); - $sql_node = $connection_node->appendChild(new DOMElement('sql')); - $sql_node->appendChild($drupal_filter_dom->createTextNode("SELECT DISTINCT u.uid AS userid, u.name AS Name, u.pass AS Pass, r.name AS Role FROM ({$prefix}users u LEFT JOIN {$prefix}users_roles ON u.uid={$prefix}users_roles.uid) LEFT JOIN {$prefix}role r ON r.rid={$prefix}users_roles.rid WHERE u.name=? AND u.pass=?;")); - file_put_contents($this->configuration['drupal_filter_file'], $drupal_filter_dom->saveXML()); - } - - /** - * Creates the a full fedora admin user with a repository connection. - */ - protected function createAdminUser() { - $roles = user_roles(); - $index = array_search('administrator', $roles); - $user = $this->drupalCreateUser(); - $user->roles[$index] = 'administrator'; - $user->name = $this->configuration['admin_user']; - $user->pass = $this->configuration['admin_pass']; - $user = user_save($user); - $url = variable_get('islandora_base_url', $this->configuration['fedora_url']); - $connection = islandora_get_tuque_connection($user, $url); - $user->repository = $connection->repository; - return $user; - } - - /** - * Logs in the given user, handles the special case where the user is admin. - * - * @see DrupalWebTestCase::drupalLogin() - */ - protected function drupalLogin(stdClass $account) { - if ($account->uid == $this->admin->uid) { - // Create password for Drupal. - $edit = array('pass' => user_password()); - $account = user_save($account, $edit); - // Raw password is used to login. - $account->pass_raw = $edit['pass']; - // We must login before changing the password for fedora. - parent::drupalLogin($account); - $account->name = $this->configuration['admin_user']; - $account->pass = $this->configuration['admin_pass']; - // Save the fedora admin credentials for later GET/POST requests. - $account = user_save($account); - } - else { - parent::drupalLogin($account); - $this->users[] = $account->name; - } - } - - - /** - * Stores the content of the Drupal Filter for later restoration. - */ - protected function restoreDrupalFilter() { - $file = $this->configuration['drupal_filter_file']; - if (isset($this->originalDrupalFilterContent)) { - file_put_contents($file, $this->originalDrupalFilterContent); - } - elseif (file_exists($file)) { - // Remove if there was never an original. - drupal_unlink($file); - } - } - - /** - * Restores the original Drupal filter, frees any allocated resources. - * - * To safeguard against leaving test objects in the repository, tearDown() - * calls deleteUserCreatedObjects() every time by default. This feature can be - * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. - * - * @see DrupalWebTestCase::tearDown() - */ - public function tearDown() { - if ($this->deleteObjectsOnTeardown) { - foreach ($this->users as $user) { - $this->deleteUserCreatedObjects($user); - } - } - if ($this->configuration['use_drupal_filter']) { - $this->restoreDrupalFilter(); - } - unset($this->admin); - unset($this->configuration); - parent::tearDown(); - } - - /** - * Asserts that the given datastreams exist correctly on the object. - * - * @param AbstractObject $object - * The PID of the object - * @param array $datastreams - * An array of strings containing datastream names - * - * @return bool - * TRUE on success, FALSE on fail. - */ - public function assertDatastreams($object, array $datastreams) { - if (!is_object($object)) { - $this->fail("Failed. Object passed in is invalid.", 'Islandora'); - } - else { - $missing_datastreams = array_diff_key(array_flip($datastreams), $this->admin->repository->api->a->listDatastreams($object->id)); - - if (!empty($missing_datastreams)) { - $this->fail("Failed to find datastream(s) " . implode(', ', array_flip($missing_datastreams)) . " in object {$object->id}."); - return FALSE; - } - - $this->pass("Found required datastream(s) in object {$object->id}"); - return TRUE; - } - } - - /** - * Attempts to validate an array of datastreams, generally via binary checks. - * - * Datastream validation classes exist in, and can be added to, the file - * 'datastream_validators.inc', which is found in this folder. Datastream - * validator classes use the naming convention 'PrefixDatastreamValidator', - * and that 'Prefix' is what this function uses to determine what class to - * instantiate. - * - * $param IslandoraFedoraObject $object - * The object to load datastreams from. - * $param array $datastreams - * An array of arrays that pair DSIDs, DatastreamValidator class prefixes, - * and optional params. You can check some of the existing implementations - * for examples. - */ - public function validateDatastreams($object, array $datastreams) { - - if (!is_object($object)) { - $this->fail("Datastream validation failed; Object passed in is invalid.", 'Islandora'); - return; - } - - module_load_include('inc', 'islandora', 'tests/datastream_validators'); - - foreach ($datastreams as $datastream) { - // Let's give them conventional names. - $dsid = $datastream[0]; - $prefix = $datastream[1]; - $params = array(); - if (isset($datastream[2])) { - $params = $datastream[2]; - } - - // Legacy tests were created before the CamelCase conventions of the class - // system now in place. So, we need to automagically seek out prefixes - // that start with a lower-case letter and convert them to the proper - // format (rather than fixing every single legacy test). - if (ctype_lower(substr($prefix, 0, 1))) { - // Handle the case where the prefix is "image". - if ($prefix === 'image') { - $prefix = 'Image'; - } - // Handle the case where the prefix is "text". - elseif ($prefix === 'text') { - $prefix = 'Text'; - } - // All other cases involve just converting everything to caps. - else { - $prefix = strtoupper($prefix); - } - } - - // Instantiate the appropriate class, and grab the results. - $class_name = "{$prefix}DatastreamValidator"; - if (class_exists($class_name)) { - $validator = new $class_name($object, $dsid, $params); - foreach ($validator->getResults() as $result) { - $this->assert($result->getType(), $result->getMessage(), 'Islandora', $result->getCaller()); - } - } - else { - $this->fail("No DatastreamValidator class was found with the name '$class_name'; are you sure the prefix given to IslandoraWebTestCase->validateDatastreams() was entered correctly, or that such a validator exists?", 'Islandora'); - } - } - } - - /** - * Gets a tuque object from a path. - * - * @param string $path - * A full or partial path to an islandora object. - * - * @return AbstractObject - * The pid of the object or FALSE if a PID is not found. - */ - public function getObjectFromPath($path) { - $path_parts = explode('/', $path); - $array_length = count($path_parts); - for ($i = 0; $i < $array_length; $i++) { - if ($path_parts[$i] == 'islandora' && isset($path_parts[$i + 1]) && $path_parts[$i + 1] == 'object') { - if (isset($path_parts[$i + 2])) { - return islandora_object_load(urldecode($path_parts[$i + 2])); - } - } - } - $this->fail("Failed to parse path: $path."); - return FALSE; - } - - /** - * Deletes an object using the PID. This does the deletion using the UI. - * - * @param string $pid - * The PID of the collection to be deleted - * @param string $button - * The label of the first 'Delete' button - * @param bool $safety - * If TRUE, this will only delete objects owned by users in $this->users. - */ - public function deleteObject($pid, $button = NULL, $safety = TRUE) { - $object = islandora_object_load($pid); - if (!$safety || in_array($object->owner, $this->users)) { - $path = "islandora/object/$pid/manage/properties"; - if (isset($button)) { - $this->drupalPost($path, array(), $button); - } - else { - $object = islandora_object_load($pid); - $this->drupalPost($path, array(), "Permanently remove '{$object->label}' from repository"); - } - $this->drupalPost($this->url, array(), t('Delete')); - - $this->drupalGet("islandora/object/$pid"); - $this->assertResponse(404, "Object $pid successfully deleted."); - } - else { - $this->fail("Cannot delete object {$pid}; it is owned by non-test user {$object->owner}, and this function was called with the safety on."); - return FALSE; - } - } - - /** - * Constructs and ingests a Fedora object and datastream(s) via tuque. - * - * All keys inside the parameter arrays for this function are optional. it - * can be run simply by calling $this->ingestConstructedObject();. - * - * @param array $properties - * An array containing object information using these keys: - * 'label' - The object label; randomized if not set. - * 'pid' - 'namespace:pid', or just 'namespace' to generate the suffix. - * 'models' - An array that can contain multiple content model PIDs, or a - * string containing a single content model PID. - * 'owner' - The object's owner. Defaults to the currently logged-in user, - * if available. It is recommended to set this to a value that can be found - * in $this->users; otherwise, this object will have to be manually deleted. - * 'parent' - The PID of the parent collection. - * @param array $datastreams - * An array containing zero or more datastream arrays that use the keys: - * 'dsid' - the datastream ID; randomized if not set. - * 'path' - The path to the file to use; defaults to fixtures/test.jpg. - * 'control_group' - The single-letter control group identifier. - * 'mimetype' - The datastream's mimetype. - * - * @return bool|array - * FALSE if the object ingest failed, or the object array if successful. - */ - public function ingestConstructedObject(array $properties = array(), array $datastreams = array()) { - $tuque = islandora_get_tuque_connection($this->admin); - $repository = $tuque->repository; - if (!isset($properties['pid'])) { - $properties['pid'] = "islandora"; - } - $object = $repository->constructObject($properties['pid']); - - // Set the object properties before ingesting it. - if (isset($properties['label'])) { - $object->label = $properties['label']; - } - else { - $properties['label'] = $this->randomName(16); - $object->label = $properties['label']; - } - - if (isset($properties['owner'])) { - $object->owner = $properties['owner']; - } - elseif ($this->loggedInUser !== FALSE) { - $object->owner = $this->loggedInUser->name; - } - - if (isset($properties['models'])) { - try { - $object->models = (array) $properties['models']; - } - catch (Exception $e) { - $this->fail("Encountered an exception when trying to add content models to {$object->id}: $e"); - return FALSE; - } - } - - $repository->ingestObject($object); - if (!$object) { - $this->fail(t("Failed to ingest object."), 'Islandora'); - return FALSE; - } - else { - $this->pass(t("Ingested object %object", array('%object' => $object->id)), 'Islandora'); - } - - // Chuck in some datastreams. - if (!empty($datastreams)) { - foreach ($datastreams as $datastream) { - if (!isset($datastream['dsid'])) { - $datastream['dsid'] = $this->randomName(8); - } - if (!isset($datastream['path'])) { - $datastream['path'] = drupal_get_path('module', 'islandora') . '/tests/fixtures/test.jpg'; - } - if (!isset($datastream['control_group'])) { - $new_datastream = $object->constructDatastream($datastream['dsid']); - } - else { - $new_datastream = $object->constructDatastream($datastream['dsid'], $datastream['control_group']); - } - $new_datastream->label = $datastream['dsid']; - if (isset($datastream['mimetype'])) { - $new_datastream->mimetype = $datastream['mimetype']; - } - $new_datastream->setContentFromFile($datastream['path']); - $object->ingestDatastream($new_datastream); - } - } - - // Add a parent relationship, if necessary. - if (isset($properties['parent'])) { - $object->relationships->add(FEDORA_RELS_EXT_URI, 'isMemberOfCollection', $properties['parent']); - } - - return $object; - } - - /** - * Deletes all objects created by the given user. - * - * To safeguard against leaving test objects in the repository, this is called - * each time DrupalTestCase::run() calls tearDown(). This feature can be - * toggled by setting $this->deleteObjectsOnTeardown to TRUE or FALSE. - * - * @param object $username - * The user whose objects we'd like to remove. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function deleteUserCreatedObjects($username) { - if ($username === $this->configuration['admin_user']) { - $this->fail("This function will under no circumstance attempt deletion of all objects owned by the configured Fedora admin user ({$this->configuration['admin_user']}), as this could irreparably damage the repository.", 'Islandora'); - return FALSE; - } - - $query = << WHERE -{ - ?object "$username" -} -QUERY; - - $objects = $this->admin->repository->ri->sparqlQuery($query); - foreach ($objects as $object) { - $loaded_object = islandora_object_load($object['object']['value']); - islandora_delete_object($loaded_object); - if ($this->assertFalse(islandora_object_load($object['object']['value']), "Object {$object['object']['value']} successfully removed from repository.", 'Islandora')) { - return FALSE; - } - return TRUE; - } - } - - /** - * These are a few quick helper functions to fill in a gap in the standard - * DrupalWebTestCase where no function exists to test for the simple existence - * or non-existence of an error. - */ - - /** - * Asserts that an error is found in $this->content. - * - * @param string $message - * The message to pass on to the results. - * @param string $group - * The group to place the result in. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function assertError($message = '', $group = 'Other') { - if (!$message) { - $message = "Error found on current page when error was expected."; - } - return $this->assertRaw("
", $message, $group); - } - - /** - * Asserts that no error is found in $this->content. - * - * @param string $message - * The message to pass on to the results. - * @param string $group - * The group to place the result in. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function assertNoError($message = '', $group = 'Other') { - if (!$message) { - $message = "No error found on current page when no error was expected."; - } - return $this->assertNoRaw("
", $message, $group); - } - - /** - * Asserts that a warning is found in $this->content. - * - * @param string $message - * The message to pass on to the results. - * @param string $group - * The group to place the result in. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function assertWarning($message = '', $group = 'Other') { - if (!$message) { - $message = "Warning found on current page when warning was expected."; - } - return $this->assertRaw("
", $message, $group); - } - - /** - * Asserts that no warning is found in $this->content. - * - * @param string $message - * The message to pass on to the results. - * @param string $group - * The group to place the result in. - * - * @return bool - * TRUE on success, FALSE on failure. - */ - public function assertNoWarning($message = '', $group = 'Other') { - if (!$message) { - $message = "No warning found on current page when no warning was expected."; - } - return $this->assertNoRaw("
", $message, $group); - } - - /** - * Makes a drupalPost() request, using the form submit button's ID. - * - * Because drupalPost() is silly and doesn't let us choose which button we'd - * like to select if multiple buttons have the same value, this function - * allows us to select whatever button we'd like based on the ID instead. - * - * This is done via the absolutely hilarious method of fudging the actual - * button labels that don't have the ID we want, so that the only one left - * with the correct label is the one with the right ID. - * - * @see DrupalWebTestCase::drupalPost() - * - * @param string $path - * Location of the post form. - * @param array $edit - * Field data in an associative array. - * @param string $submit - * Value of the submit button whose click is to be emulated. - * @param string $id - * ID of the submit button whose click is to be emulated. - * @param array $options - * Options to be forwarded to url(). - * @param array $headers - * An array containing additional HTTP request headers, each formatted as - * "name: value". - * @param null $form_html_id - * (optional) HTML ID of the form to be submitted. - * @param null $extra_post - * (optional) A string of additional data to append to the POST submission. - * - * @return bool|string - * The content from the POST request's curlExec, or FALSE on fail. - */ - public function drupalPostByID($path, $edit, $submit, $id, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) { - $buttons = $this->xpath("//input[@type=\"submit\" and @value=\"{$submit}\"]"); - if (empty($buttons)) { - $this->fail("No buttons found on the page with value '$submit'"); - return FALSE; - } - $i = 0; - foreach ($buttons as $button) { - if ($button['id'] !== $id) { - $button['value'] .= "_$i"; - $i++; - } - } - return $this->drupalPost($path, $edit, $submit, $options, $headers, $form_html_id, $extra_post); - } - -} diff --git a/tests/scripts/travis_setup.sh b/tests/scripts/travis_setup.sh index 8cee24dd..0b33d4b0 100755 --- a/tests/scripts/travis_setup.sh +++ b/tests/scripts/travis_setup.sh @@ -8,6 +8,7 @@ cd $HOME git clone git://github.com/Islandora/tuque.git git clone -b $FEDORA_VERSION git://github.com/Islandora/islandora_tomcat.git cd islandora_tomcat +git fsck export CATALINA_HOME='.' export JAVA_OPTS="-Xms1024m -Xmx1024m -XX:MaxPermSize=512m -XX:+CMSClassUnloadingEnabled -Djavax.net.ssl.trustStore=$CATALINA_HOME/fedora/server/truststore -Djavax.net.ssl.trustStorePassword=tomcat" ./bin/startup.sh @@ -31,7 +32,7 @@ drush si minimal --db-url=mysql://drupal:drupal@localhost/drupal --yes sudo chmod a+w sites/default/settings.php echo "include_once '$HOME/.composer/vendor/autoload.php';" >> sites/default/settings.php sudo chmod a-w sites/default/settings.php -drush runserver --php-cgi=$HOME/.phpenv/shims/php-cgi localhost:8081 &>/dev/null & +drush runserver --php-cgi=$HOME/.phpenv/shims/php-cgi localhost:8081 &>/tmp/drush_webserver.log & ln -s $ISLANDORA_DIR sites/all/modules/islandora mv sites/all/modules/islandora/tests/travis.test_config.ini sites/all/modules/islandora/tests/test_config.ini mkdir sites/all/libraries