| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Tests for image.module. 6 */ 7 8 /** 9 * TODO: Test the following functions. 10 * 11 * image.effects.inc: 12 * image_style_generate() 13 * image_style_create_derivative() 14 * 15 * image.module: 16 * image_style_load() 17 * image_style_save() 18 * image_style_delete() 19 * image_style_options() 20 * image_style_flush() 21 * image_effect_definition_load() 22 * image_effect_load() 23 * image_effect_save() 24 * image_effect_delete() 25 * image_filter_keyword() 26 */ 27 28 /** 29 * This class provides methods specifically for testing Image's field handling. 30 */ 31 class ImageFieldTestCase extends DrupalWebTestCase { 32 protected $admin_user; 33 34 function setUp() { 35 parent::setUp('image'); 36 $this->admin_user = $this->drupalCreateUser(array('access content', 'access administration pages', 'administer site configuration', 'administer content types', 'administer nodes', 'create article content', 'edit any article content', 'delete any article content', 'administer image styles')); 37 $this->drupalLogin($this->admin_user); 38 } 39 40 /** 41 * Create a new image field. 42 * 43 * @param $name 44 * The name of the new field (all lowercase), exclude the "field_" prefix. 45 * @param $type_name 46 * The node type that this field will be added to. 47 * @param $field_settings 48 * A list of field settings that will be added to the defaults. 49 * @param $instance_settings 50 * A list of instance settings that will be added to the instance defaults. 51 * @param $widget_settings 52 * A list of widget settings that will be added to the widget defaults. 53 */ 54 function createImageField($name, $type_name, $field_settings = array(), $instance_settings = array(), $widget_settings = array()) { 55 $field = array( 56 'field_name' => $name, 57 'type' => 'image', 58 'settings' => array(), 59 'cardinality' => !empty($field_settings['cardinality']) ? $field_settings['cardinality'] : 1, 60 ); 61 $field['settings'] = array_merge($field['settings'], $field_settings); 62 field_create_field($field); 63 64 $instance = array( 65 'field_name' => $field['field_name'], 66 'entity_type' => 'node', 67 'label' => $name, 68 'bundle' => $type_name, 69 'required' => !empty($instance_settings['required']), 70 'settings' => array(), 71 'widget' => array( 72 'type' => 'image_image', 73 'settings' => array(), 74 ), 75 ); 76 $instance['settings'] = array_merge($instance['settings'], $instance_settings); 77 $instance['widget']['settings'] = array_merge($instance['widget']['settings'], $widget_settings); 78 return field_create_instance($instance); 79 } 80 81 /** 82 * Upload an image to a node. 83 * 84 * @param $image 85 * A file object representing the image to upload. 86 * @param $field_name 87 * Name of the image field the image should be attached to. 88 * @param $type 89 * The type of node to create. 90 */ 91 function uploadNodeImage($image, $field_name, $type) { 92 $edit = array( 93 'title' => $this->randomName(), 94 ); 95 $edit['files[' . $field_name . '_' . LANGUAGE_NONE . '_0]'] = drupal_realpath($image->uri); 96 $this->drupalPost('node/add/' . $type, $edit, t('Save')); 97 98 // Retrieve ID of the newly created node from the current URL. 99 $matches = array(); 100 preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches); 101 return isset($matches[1]) ? $matches[1] : FALSE; 102 } 103 } 104 105 /** 106 * Tests the functions for generating paths and URLs for image styles. 107 */ 108 class ImageStylesPathAndUrlTestCase extends DrupalWebTestCase { 109 protected $style_name; 110 protected $image_info; 111 protected $image_filepath; 112 113 public static function getInfo() { 114 return array( 115 'name' => 'Image styles path and URL functions', 116 'description' => 'Tests functions for generating paths and URLs to image styles.', 117 'group' => 'Image', 118 ); 119 } 120 121 function setUp() { 122 parent::setUp('image_module_test'); 123 124 $this->style_name = 'style_foo'; 125 image_style_save(array('name' => $this->style_name)); 126 } 127 128 /** 129 * Test image_style_path(). 130 */ 131 function testImageStylePath() { 132 $scheme = 'public'; 133 $actual = image_style_path($this->style_name, "$scheme://foo/bar.gif"); 134 $expected = "$scheme://styles/" . $this->style_name . "/$scheme/foo/bar.gif"; 135 $this->assertEqual($actual, $expected, t('Got the path for a file URI.')); 136 137 $actual = image_style_path($this->style_name, 'foo/bar.gif'); 138 $expected = "$scheme://styles/" . $this->style_name . "/$scheme/foo/bar.gif"; 139 $this->assertEqual($actual, $expected, t('Got the path for a relative file path.')); 140 } 141 142 /** 143 * Test image_style_url() with a file using the "public://" scheme. 144 */ 145 function testImageStyleUrlAndPathPublic() { 146 $this->_testImageStyleUrlAndPath('public'); 147 } 148 149 /** 150 * Test image_style_url() with a file using the "private://" scheme. 151 */ 152 function testImageStyleUrlAndPathPrivate() { 153 $this->_testImageStyleUrlAndPath('private'); 154 } 155 156 /** 157 * Test image_style_url() with the "public://" scheme and unclean URLs. 158 */ 159 function testImageStylUrlAndPathPublicUnclean() { 160 $this->_testImageStyleUrlAndPath('public', FALSE); 161 } 162 163 /** 164 * Test image_style_url() with the "private://" schema and unclean URLs. 165 */ 166 function testImageStyleUrlAndPathPrivateUnclean() { 167 $this->_testImageStyleUrlAndPath('private', FALSE); 168 } 169 170 /** 171 * Test image_style_url(). 172 */ 173 function _testImageStyleUrlAndPath($scheme, $clean_url = TRUE) { 174 // Make the default scheme neither "public" nor "private" to verify the 175 // functions work for other than the default scheme. 176 variable_set('file_default_scheme', 'temporary'); 177 variable_set('clean_url', $clean_url); 178 179 // Create the directories for the styles. 180 $directory = $scheme . '://styles/' . $this->style_name; 181 $status = file_prepare_directory($directory, FILE_CREATE_DIRECTORY); 182 $this->assertNotIdentical(FALSE, $status, t('Created the directory for the generated images for the test style.')); 183 184 // Create a working copy of the file. 185 $files = $this->drupalGetTestFiles('image'); 186 $file = array_shift($files); 187 $image_info = image_get_info($file->uri); 188 $original_uri = file_unmanaged_copy($file->uri, $scheme . '://', FILE_EXISTS_RENAME); 189 // Let the image_module_test module know about this file, so it can claim 190 // ownership in hook_file_download(). 191 variable_set('image_module_test_file_download', $original_uri); 192 $this->assertNotIdentical(FALSE, $original_uri, t('Created the generated image file.')); 193 194 // Get the URL of a file that has not been generated and try to create it. 195 $generated_uri = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/'. drupal_basename($original_uri); 196 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 197 $generate_url = image_style_url($this->style_name, $original_uri); 198 199 if (!$clean_url) { 200 $this->assertTrue(strpos($generate_url, '?q=') !== FALSE, 'When using non-clean URLS, the system path contains the query string.'); 201 } 202 203 // Fetch the URL that generates the file. 204 $this->drupalGet($generate_url); 205 $this->assertResponse(200, t('Image was generated at the URL.')); 206 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 207 $this->assertRaw(file_get_contents($generated_uri), t('URL returns expected file.')); 208 $generated_image_info = image_get_info($generated_uri); 209 $this->assertEqual($this->drupalGetHeader('Content-Type'), $generated_image_info['mime_type'], t('Expected Content-Type was reported.')); 210 $this->assertEqual($this->drupalGetHeader('Content-Length'), $generated_image_info['file_size'], t('Expected Content-Length was reported.')); 211 if ($scheme == 'private') { 212 $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', t('Expires header was sent.')); 213 $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'no-cache, must-revalidate, post-check=0, pre-check=0', t('Cache-Control header was set to prevent caching.')); 214 $this->assertEqual($this->drupalGetHeader('X-Image-Owned-By'), 'image_module_test', t('Expected custom header has been added.')); 215 216 // Make sure that a second request to the already existing derivate works 217 // too. 218 $this->drupalGet($generate_url); 219 $this->assertResponse(200, t('Image was generated at the URL.')); 220 221 // Repeat this with a different file that we do not have access to and 222 // make sure that access is denied. 223 $file_noaccess = array_shift($files); 224 $original_uri_noaccess = file_unmanaged_copy($file_noaccess->uri, $scheme . '://', FILE_EXISTS_RENAME); 225 $generated_uri_noaccess = $scheme . '://styles/' . $this->style_name . '/' . $scheme . '/'. drupal_basename($original_uri_noaccess); 226 $this->assertFalse(file_exists($generated_uri_noaccess), t('Generated file does not exist.')); 227 $generate_url_noaccess = image_style_url($this->style_name, $original_uri_noaccess); 228 229 $this->drupalGet($generate_url_noaccess); 230 $this->assertResponse(403, t('Confirmed that access is denied for the private image style.') ); 231 // Verify that images are not appended to the response. Currently this test only uses PNG images. 232 if (strpos($generate_url, '.png') === FALSE ) { 233 $this->fail('Confirming that private image styles are not appended require PNG file.'); 234 } 235 else { 236 // Check for PNG-Signature (cf. http://www.libpng.org/pub/png/book/chapter08.html#png.ch08.div.2) in the 237 // response body. 238 $this->assertNoRaw( chr(137) . chr(80) . chr(78) . chr(71) . chr(13) . chr(10) . chr(26) . chr(10), 'No PNG signature found in the response body.'); 239 } 240 } 241 } 242 } 243 244 /** 245 * Use the image_test.module's mock toolkit to ensure that the effects are 246 * properly passing parameters to the image toolkit. 247 */ 248 class ImageEffectsUnitTest extends ImageToolkitTestCase { 249 public static function getInfo() { 250 return array( 251 'name' => 'Image effects', 252 'description' => 'Test that the image effects pass parameters to the toolkit correctly.', 253 'group' => 'Image', 254 ); 255 } 256 257 function setUp() { 258 parent::setUp('image_module_test'); 259 module_load_include('inc', 'image', 'image.effects'); 260 } 261 262 /** 263 * Test the image_resize_effect() function. 264 */ 265 function testResizeEffect() { 266 $this->assertTrue(image_resize_effect($this->image, array('width' => 1, 'height' => 2)), t('Function returned the expected value.')); 267 $this->assertToolkitOperationsCalled(array('resize')); 268 269 // Check the parameters. 270 $calls = image_test_get_all_calls(); 271 $this->assertEqual($calls['resize'][0][1], 1, t('Width was passed correctly')); 272 $this->assertEqual($calls['resize'][0][2], 2, t('Height was passed correctly')); 273 } 274 275 /** 276 * Test the image_scale_effect() function. 277 */ 278 function testScaleEffect() { 279 // @todo: need to test upscaling. 280 $this->assertTrue(image_scale_effect($this->image, array('width' => 10, 'height' => 10)), t('Function returned the expected value.')); 281 $this->assertToolkitOperationsCalled(array('resize')); 282 283 // Check the parameters. 284 $calls = image_test_get_all_calls(); 285 $this->assertEqual($calls['resize'][0][1], 10, t('Width was passed correctly')); 286 $this->assertEqual($calls['resize'][0][2], 5, t('Height was based off aspect ratio and passed correctly')); 287 } 288 289 /** 290 * Test the image_crop_effect() function. 291 */ 292 function testCropEffect() { 293 // @todo should test the keyword offsets. 294 $this->assertTrue(image_crop_effect($this->image, array('anchor' => 'top-1', 'width' => 3, 'height' => 4)), t('Function returned the expected value.')); 295 $this->assertToolkitOperationsCalled(array('crop')); 296 297 // Check the parameters. 298 $calls = image_test_get_all_calls(); 299 $this->assertEqual($calls['crop'][0][1], 0, t('X was passed correctly')); 300 $this->assertEqual($calls['crop'][0][2], 1, t('Y was passed correctly')); 301 $this->assertEqual($calls['crop'][0][3], 3, t('Width was passed correctly')); 302 $this->assertEqual($calls['crop'][0][4], 4, t('Height was passed correctly')); 303 } 304 305 /** 306 * Test the image_scale_and_crop_effect() function. 307 */ 308 function testScaleAndCropEffect() { 309 $this->assertTrue(image_scale_and_crop_effect($this->image, array('width' => 5, 'height' => 10)), t('Function returned the expected value.')); 310 $this->assertToolkitOperationsCalled(array('resize', 'crop')); 311 312 // Check the parameters. 313 $calls = image_test_get_all_calls(); 314 $this->assertEqual($calls['crop'][0][1], 7.5, t('X was computed and passed correctly')); 315 $this->assertEqual($calls['crop'][0][2], 0, t('Y was computed and passed correctly')); 316 $this->assertEqual($calls['crop'][0][3], 5, t('Width was computed and passed correctly')); 317 $this->assertEqual($calls['crop'][0][4], 10, t('Height was computed and passed correctly')); 318 } 319 320 /** 321 * Test the image_desaturate_effect() function. 322 */ 323 function testDesaturateEffect() { 324 $this->assertTrue(image_desaturate_effect($this->image, array()), t('Function returned the expected value.')); 325 $this->assertToolkitOperationsCalled(array('desaturate')); 326 327 // Check the parameters. 328 $calls = image_test_get_all_calls(); 329 $this->assertEqual(count($calls['desaturate'][0]), 1, t('Only the image was passed.')); 330 } 331 332 /** 333 * Test the image_rotate_effect() function. 334 */ 335 function testRotateEffect() { 336 // @todo: need to test with 'random' => TRUE 337 $this->assertTrue(image_rotate_effect($this->image, array('degrees' => 90, 'bgcolor' => '#fff')), t('Function returned the expected value.')); 338 $this->assertToolkitOperationsCalled(array('rotate')); 339 340 // Check the parameters. 341 $calls = image_test_get_all_calls(); 342 $this->assertEqual($calls['rotate'][0][1], 90, t('Degrees were passed correctly')); 343 $this->assertEqual($calls['rotate'][0][2], 0xffffff, t('Background color was passed correctly')); 344 } 345 346 /** 347 * Test image effect caching. 348 */ 349 function testImageEffectsCaching() { 350 $image_effect_definitions_called = &drupal_static('image_module_test_image_effect_info_alter'); 351 352 // First call should grab a fresh copy of the data. 353 $effects = image_effect_definitions(); 354 $this->assertTrue($image_effect_definitions_called === 1, 'image_effect_definitions() generated data.'); 355 356 // Second call should come from cache. 357 drupal_static_reset('image_effect_definitions'); 358 drupal_static_reset('image_module_test_image_effect_info_alter'); 359 $cached_effects = image_effect_definitions(); 360 $this->assertTrue(is_null($image_effect_definitions_called), 'image_effect_definitions() returned data from cache.'); 361 362 $this->assertTrue($effects == $cached_effects, 'Cached effects are the same as generated effects.'); 363 } 364 } 365 366 /** 367 * Tests creation, deletion, and editing of image styles and effects. 368 */ 369 class ImageAdminStylesUnitTest extends ImageFieldTestCase { 370 371 public static function getInfo() { 372 return array( 373 'name' => 'Image styles and effects UI configuration', 374 'description' => 'Tests creation, deletion, and editing of image styles and effects at the UI level.', 375 'group' => 'Image', 376 ); 377 } 378 379 /** 380 * Given an image style, generate an image. 381 */ 382 function createSampleImage($style) { 383 static $file_path; 384 385 // First, we need to make sure we have an image in our testing 386 // file directory. Copy over an image on the first run. 387 if (!isset($file_path)) { 388 $files = $this->drupalGetTestFiles('image'); 389 $file = reset($files); 390 $file_path = file_unmanaged_copy($file->uri); 391 } 392 393 return image_style_url($style['name'], $file_path) ? $file_path : FALSE; 394 } 395 396 /** 397 * Count the number of images currently create for a style. 398 */ 399 function getImageCount($style) { 400 return count(file_scan_directory('public://styles/' . $style['name'], '/.*/')); 401 } 402 403 /** 404 * Test creating an image style with a numeric name and ensuring it can be 405 * applied to an image. 406 */ 407 function testNumericStyleName() { 408 $style_name = rand(); 409 $edit = array( 410 'name' => $style_name, 411 ); 412 $this->drupalPost('admin/config/media/image-styles/add', $edit, t('Create new style')); 413 $this->assertRaw(t('Style %name was created.', array('%name' => $style_name)), t('Image style successfully created.')); 414 $options = image_style_options(); 415 $this->assertTrue(array_key_exists($style_name, $options), t('Array key %key exists.', array('%key' => $style_name))); 416 } 417 418 /** 419 * General test to add a style, add/remove/edit effects to it, then delete it. 420 */ 421 function testStyle() { 422 // Setup a style to be created and effects to add to it. 423 $style_name = strtolower($this->randomName(10)); 424 $style_path = 'admin/config/media/image-styles/edit/' . $style_name; 425 $effect_edits = array( 426 'image_resize' => array( 427 'data[width]' => 100, 428 'data[height]' => 101, 429 ), 430 'image_scale' => array( 431 'data[width]' => 110, 432 'data[height]' => 111, 433 'data[upscale]' => 1, 434 ), 435 'image_scale_and_crop' => array( 436 'data[width]' => 120, 437 'data[height]' => 121, 438 ), 439 'image_crop' => array( 440 'data[width]' => 130, 441 'data[height]' => 131, 442 'data[anchor]' => 'center-center', 443 ), 444 'image_desaturate' => array( 445 // No options for desaturate. 446 ), 447 'image_rotate' => array( 448 'data[degrees]' => 5, 449 'data[random]' => 1, 450 'data[bgcolor]' => '#FFFF00', 451 ), 452 ); 453 454 // Add style form. 455 456 $edit = array( 457 'name' => $style_name, 458 ); 459 $this->drupalPost('admin/config/media/image-styles/add', $edit, t('Create new style')); 460 $this->assertRaw(t('Style %name was created.', array('%name' => $style_name)), t('Image style successfully created.')); 461 462 // Add effect form. 463 464 // Add each sample effect to the style. 465 foreach ($effect_edits as $effect => $edit) { 466 // Add the effect. 467 $this->drupalPost($style_path, array('new' => $effect), t('Add')); 468 if (!empty($edit)) { 469 $this->drupalPost(NULL, $edit, t('Add effect')); 470 } 471 } 472 473 // Edit effect form. 474 475 // Revisit each form to make sure the effect was saved. 476 drupal_static_reset('image_styles'); 477 $style = image_style_load($style_name); 478 479 foreach ($style['effects'] as $ieid => $effect) { 480 $this->drupalGet($style_path . '/effects/' . $ieid); 481 foreach ($effect_edits[$effect['name']] as $field => $value) { 482 $this->assertFieldByName($field, $value, t('The %field field in the %effect effect has the correct value of %value.', array('%field' => $field, '%effect' => $effect['name'], '%value' => $value))); 483 } 484 } 485 486 // Image style overview form (ordering and renaming). 487 488 // Confirm the order of effects is maintained according to the order we 489 // added the fields. 490 $effect_edits_order = array_keys($effect_edits); 491 $effects_order = array_values($style['effects']); 492 $order_correct = TRUE; 493 foreach ($effects_order as $index => $effect) { 494 if ($effect_edits_order[$index] != $effect['name']) { 495 $order_correct = FALSE; 496 } 497 } 498 $this->assertTrue($order_correct, t('The order of the effects is correctly set by default.')); 499 500 // Test the style overview form. 501 // Change the name of the style and adjust the weights of effects. 502 $style_name = strtolower($this->randomName(10)); 503 $weight = count($effect_edits); 504 $edit = array( 505 'name' => $style_name, 506 ); 507 foreach ($style['effects'] as $ieid => $effect) { 508 $edit['effects[' . $ieid . '][weight]'] = $weight; 509 $weight--; 510 } 511 512 // Create an image to make sure it gets flushed after saving. 513 $image_path = $this->createSampleImage($style); 514 $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path))); 515 516 $this->drupalPost($style_path, $edit, t('Update style')); 517 518 // Note that after changing the style name, the style path is changed. 519 $style_path = 'admin/config/media/image-styles/edit/' . $style_name; 520 521 // Check that the URL was updated. 522 $this->drupalGet($style_path); 523 $this->assertResponse(200, t('Image style %original renamed to %new', array('%original' => $style['name'], '%new' => $style_name))); 524 525 // Check that the image was flushed after updating the style. 526 // This is especially important when renaming the style. Make sure that 527 // the old image directory has been deleted. 528 $this->assertEqual($this->getImageCount($style), 0, t('Image style %style was flushed after renaming the style and updating the order of effects.', array('%style' => $style['name']))); 529 530 // Load the style by the new name with the new weights. 531 drupal_static_reset('image_styles'); 532 $style = image_style_load($style_name, NULL); 533 534 // Confirm the new style order was saved. 535 $effect_edits_order = array_reverse($effect_edits_order); 536 $effects_order = array_values($style['effects']); 537 $order_correct = TRUE; 538 foreach ($effects_order as $index => $effect) { 539 if ($effect_edits_order[$index] != $effect['name']) { 540 $order_correct = FALSE; 541 } 542 } 543 $this->assertTrue($order_correct, t('The order of the effects is correctly set by default.')); 544 545 // Image effect deletion form. 546 547 // Create an image to make sure it gets flushed after deleting an effect. 548 $image_path = $this->createSampleImage($style); 549 $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path))); 550 551 // Test effect deletion form. 552 $effect = array_pop($style['effects']); 553 $this->drupalPost($style_path . '/effects/' . $effect['ieid'] . '/delete', array(), t('Delete')); 554 $this->assertRaw(t('The image effect %name has been deleted.', array('%name' => $effect['label'])), t('Image effect deleted.')); 555 556 // Style deletion form. 557 558 // Delete the style. 559 $this->drupalPost('admin/config/media/image-styles/delete/' . $style_name, array(), t('Delete')); 560 561 // Confirm the style directory has been removed. 562 $directory = file_default_scheme() . '://styles/' . $style_name; 563 $this->assertFalse(is_dir($directory), t('Image style %style directory removed on style deletion.', array('%style' => $style['name']))); 564 565 drupal_static_reset('image_styles'); 566 $this->assertFalse(image_style_load($style_name), t('Image style %style successfully deleted.', array('%style' => $style['name']))); 567 568 } 569 570 /** 571 * Test to override, edit, then revert a style. 572 */ 573 function testDefaultStyle() { 574 // Setup a style to be created and effects to add to it. 575 $style_name = 'thumbnail'; 576 $edit_path = 'admin/config/media/image-styles/edit/' . $style_name; 577 $delete_path = 'admin/config/media/image-styles/delete/' . $style_name; 578 $revert_path = 'admin/config/media/image-styles/revert/' . $style_name; 579 580 // Ensure deleting a default is not possible. 581 $this->drupalGet($delete_path); 582 $this->assertText(t('Page not found'), t('Default styles may not be deleted.')); 583 584 // Ensure that editing a default is not possible (without overriding). 585 $this->drupalGet($edit_path); 586 $this->assertNoField('edit-name', t('Default styles may not be renamed.')); 587 $this->assertNoField('edit-submit', t('Default styles may not be edited.')); 588 $this->assertNoField('edit-add', t('Default styles may not have new effects added.')); 589 590 // Create an image to make sure the default works before overriding. 591 drupal_static_reset('image_styles'); 592 $style = image_style_load($style_name); 593 $image_path = $this->createSampleImage($style); 594 $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path))); 595 596 // Verify that effects attached to a default style do not have an ieid key. 597 foreach ($style['effects'] as $effect) { 598 $this->assertFalse(isset($effect['ieid']), t('The %effect effect does not have an ieid.', array('%effect' => $effect['name']))); 599 } 600 601 // Override the default. 602 $this->drupalPost($edit_path, array(), t('Override defaults')); 603 $this->assertRaw(t('The %style style has been overridden, allowing you to change its settings.', array('%style' => $style_name)), t('Default image style may be overridden.')); 604 605 // Add sample effect to the overridden style. 606 $this->drupalPost($edit_path, array('new' => 'image_desaturate'), t('Add')); 607 drupal_static_reset('image_styles'); 608 $style = image_style_load($style_name); 609 610 // Verify that effects attached to the style have an ieid now. 611 foreach ($style['effects'] as $effect) { 612 $this->assertTrue(isset($effect['ieid']), t('The %effect effect has an ieid.', array('%effect' => $effect['name']))); 613 } 614 615 // The style should now have 2 effect, the original scale provided by core 616 // and the desaturate effect we added in the override. 617 $effects = array_values($style['effects']); 618 $this->assertEqual($effects[0]['name'], 'image_scale', t('The default effect still exists in the overridden style.')); 619 $this->assertEqual($effects[1]['name'], 'image_desaturate', t('The added effect exists in the overridden style.')); 620 621 // Check that we are unable to rename an overridden style. 622 $this->drupalGet($edit_path); 623 $this->assertNoField('edit-name', t('Overridden styles may not be renamed.')); 624 625 // Create an image to ensure the override works properly. 626 $image_path = $this->createSampleImage($style); 627 $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path))); 628 629 // Revert the image style. 630 $this->drupalPost($revert_path, array(), t('Revert')); 631 drupal_static_reset('image_styles'); 632 $style = image_style_load($style_name); 633 634 // The style should now have the single effect for scale. 635 $effects = array_values($style['effects']); 636 $this->assertEqual($effects[0]['name'], 'image_scale', t('The default effect still exists in the reverted style.')); 637 $this->assertFalse(array_key_exists(1, $effects), t('The added effect has been removed in the reverted style.')); 638 } 639 640 /** 641 * Test deleting a style and choosing a replacement style. 642 */ 643 function testStyleReplacement() { 644 // Create a new style. 645 $style_name = strtolower($this->randomName(10)); 646 image_style_save(array('name' => $style_name)); 647 $style_path = 'admin/config/media/image-styles/edit/' . $style_name; 648 649 // Create an image field that uses the new style. 650 $field_name = strtolower($this->randomName(10)); 651 $this->createImageField($field_name, 'article'); 652 $instance = field_info_instance('node', $field_name, 'article'); 653 $instance['display']['default']['type'] = 'image'; 654 $instance['display']['default']['settings']['image_style'] = $style_name; 655 field_update_instance($instance); 656 657 // Create a new node with an image attached. 658 $test_image = current($this->drupalGetTestFiles('image')); 659 $nid = $this->uploadNodeImage($test_image, $field_name, 'article'); 660 $node = node_load($nid); 661 662 // Test that image is displayed using newly created style. 663 $this->drupalGet('node/' . $nid); 664 $this->assertRaw(image_style_url($style_name, $node->{$field_name}[LANGUAGE_NONE][0]['uri']), t('Image displayed using style @style.', array('@style' => $style_name))); 665 666 // Rename the style and make sure the image field is updated. 667 $new_style_name = strtolower($this->randomName(10)); 668 $edit = array( 669 'name' => $new_style_name, 670 ); 671 $this->drupalPost('admin/config/media/image-styles/edit/' . $style_name, $edit, t('Update style')); 672 $this->assertText(t('Changes to the style have been saved.'), t('Style %name was renamed to %new_name.', array('%name' => $style_name, '%new_name' => $new_style_name))); 673 $this->drupalGet('node/' . $nid); 674 $this->assertRaw(image_style_url($new_style_name, $node->{$field_name}[LANGUAGE_NONE][0]['uri']), t('Image displayed using style replacement style.')); 675 676 // Delete the style and choose a replacement style. 677 $edit = array( 678 'replacement' => 'thumbnail', 679 ); 680 $this->drupalPost('admin/config/media/image-styles/delete/' . $new_style_name, $edit, t('Delete')); 681 $message = t('Style %name was deleted.', array('%name' => $new_style_name)); 682 $this->assertRaw($message, $message); 683 684 $this->drupalGet('node/' . $nid); 685 $this->assertRaw(image_style_url('thumbnail', $node->{$field_name}[LANGUAGE_NONE][0]['uri']), t('Image displayed using style replacement style.')); 686 } 687 } 688 689 /** 690 * Test class to check that formatters and display settings are working. 691 */ 692 class ImageFieldDisplayTestCase extends ImageFieldTestCase { 693 public static function getInfo() { 694 return array( 695 'name' => 'Image field display tests', 696 'description' => 'Test the display of image fields.', 697 'group' => 'Image', 698 ); 699 } 700 701 /** 702 * Test image formatters on node display for public files. 703 */ 704 function testImageFieldFormattersPublic() { 705 $this->_testImageFieldFormatters('public'); 706 } 707 708 /** 709 * Test image formatters on node display for private files. 710 */ 711 function testImageFieldFormattersPrivate() { 712 // Remove access content permission from anonymous users. 713 user_role_change_permissions(DRUPAL_ANONYMOUS_RID, array('access content' => FALSE)); 714 $this->_testImageFieldFormatters('private'); 715 } 716 717 /** 718 * Test image formatters on node display. 719 */ 720 function _testImageFieldFormatters($scheme) { 721 $field_name = strtolower($this->randomName()); 722 $this->createImageField($field_name, 'article', array('uri_scheme' => $scheme)); 723 // Create a new node with an image attached. 724 $test_image = current($this->drupalGetTestFiles('image')); 725 $nid = $this->uploadNodeImage($test_image, $field_name, 'article'); 726 $node = node_load($nid, NULL, TRUE); 727 728 // Test that the default formatter is being used. 729 $image_uri = $node->{$field_name}[LANGUAGE_NONE][0]['uri']; 730 $image_info = array( 731 'path' => $image_uri, 732 'width' => 40, 733 'height' => 20, 734 ); 735 $default_output = theme('image', $image_info); 736 $this->assertRaw($default_output, t('Default formatter displaying correctly on full node view.')); 737 738 // Test the image linked to file formatter. 739 $instance = field_info_instance('node', $field_name, 'article'); 740 $instance['display']['default']['type'] = 'image'; 741 $instance['display']['default']['settings']['image_link'] = 'file'; 742 field_update_instance($instance); 743 $default_output = l(theme('image', $image_info), file_create_url($image_uri), array('html' => TRUE)); 744 $this->drupalGet('node/' . $nid); 745 $this->assertRaw($default_output, t('Image linked to file formatter displaying correctly on full node view.')); 746 // Verify that the image can be downloaded. 747 $this->assertEqual(file_get_contents($test_image->uri), $this->drupalGet(file_create_url($image_uri)), t('File was downloaded successfully.')); 748 if ($scheme == 'private') { 749 // Only verify HTTP headers when using private scheme and the headers are 750 // sent by Drupal. 751 $this->assertEqual($this->drupalGetHeader('Content-Type'), 'image/png', t('Content-Type header was sent.')); 752 $this->assertEqual($this->drupalGetHeader('Content-Disposition'), 'inline; filename="' . $test_image->filename . '"', t('Content-Disposition header was sent.')); 753 $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'private', t('Cache-Control header was sent.')); 754 755 // Log out and try to access the file. 756 $this->drupalLogout(); 757 $this->drupalGet(file_create_url($image_uri)); 758 $this->assertResponse('403', t('Access denied to original image as anonymous user.')); 759 760 // Log in again. 761 $this->drupalLogin($this->admin_user); 762 } 763 764 // Test the image linked to content formatter. 765 $instance['display']['default']['settings']['image_link'] = 'content'; 766 field_update_instance($instance); 767 $default_output = l(theme('image', $image_info), 'node/' . $nid, array('html' => TRUE, 'attributes' => array('class' => 'active'))); 768 $this->drupalGet('node/' . $nid); 769 $this->assertRaw($default_output, t('Image linked to content formatter displaying correctly on full node view.')); 770 771 // Test the image style 'thumbnail' formatter. 772 $instance['display']['default']['settings']['image_link'] = ''; 773 $instance['display']['default']['settings']['image_style'] = 'thumbnail'; 774 field_update_instance($instance); 775 // Ensure the derivative image is generated so we do not have to deal with 776 // image style callback paths. 777 $this->drupalGet(image_style_url('thumbnail', $image_uri)); 778 $image_info['path'] = image_style_path('thumbnail', $image_uri); 779 $image_info['width'] = 100; 780 $image_info['height'] = 50; 781 $default_output = theme('image', $image_info); 782 $this->drupalGet('node/' . $nid); 783 $this->assertRaw($default_output, t('Image style thumbnail formatter displaying correctly on full node view.')); 784 785 if ($scheme == 'private') { 786 // Log out and try to access the file. 787 $this->drupalLogout(); 788 $this->drupalGet(image_style_url('thumbnail', $image_uri)); 789 $this->assertResponse('403', t('Access denied to image style thumbnail as anonymous user.')); 790 } 791 } 792 793 /** 794 * Tests for image field settings. 795 */ 796 function testImageFieldSettings() { 797 $test_image = current($this->drupalGetTestFiles('image')); 798 list(, $test_image_extension) = explode('.', $test_image->filename); 799 $field_name = strtolower($this->randomName()); 800 $instance_settings = array( 801 'alt_field' => 1, 802 'file_extensions' => $test_image_extension, 803 'max_filesize' => '50 KB', 804 'max_resolution' => '100x100', 805 'min_resolution' => '10x10', 806 'title_field' => 1, 807 ); 808 $widget_settings = array( 809 'preview_image_style' => 'medium', 810 ); 811 $field = $this->createImageField($field_name, 'article', array(), $instance_settings, $widget_settings); 812 $field['deleted'] = 0; 813 $table = _field_sql_storage_tablename($field); 814 $schema = drupal_get_schema($table, TRUE); 815 $instance = field_info_instance('node', $field_name, 'article'); 816 817 $this->drupalGet('node/add/article'); 818 $this->assertText(t('Files must be less than 50 KB.'), t('Image widget max file size is displayed on article form.')); 819 $this->assertText(t('Allowed file types: ' . $test_image_extension . '.'), t('Image widget allowed file types displayed on article form.')); 820 $this->assertText(t('Images must be between 10x10 and 100x100 pixels.'), t('Image widget allowed resolution displayed on article form.')); 821 822 // We have to create the article first and then edit it because the alt 823 // and title fields do not display until the image has been attached. 824 $nid = $this->uploadNodeImage($test_image, $field_name, 'article'); 825 $this->drupalGet('node/' . $nid . '/edit'); 826 $this->assertFieldByName($field_name . '[' . LANGUAGE_NONE . '][0][alt]', '', t('Alt field displayed on article form.')); 827 $this->assertFieldByName($field_name . '[' . LANGUAGE_NONE . '][0][title]', '', t('Title field displayed on article form.')); 828 // Verify that the attached image is being previewed using the 'medium' 829 // style. 830 $node = node_load($nid, NULL, TRUE); 831 $image_info = array( 832 'path' => image_style_url('medium', $node->{$field_name}[LANGUAGE_NONE][0]['uri']), 833 'width' => 220, 834 'height' => 110, 835 ); 836 $default_output = theme('image', $image_info); 837 $this->assertRaw($default_output, t("Preview image is displayed using 'medium' style.")); 838 839 // Add alt/title fields to the image and verify that they are displayed. 840 $image_info = array( 841 'path' => $node->{$field_name}[LANGUAGE_NONE][0]['uri'], 842 'alt' => $this->randomName(), 843 'title' => $this->randomName(), 844 'width' => 40, 845 'height' => 20, 846 ); 847 $edit = array( 848 $field_name . '[' . LANGUAGE_NONE . '][0][alt]' => $image_info['alt'], 849 $field_name . '[' . LANGUAGE_NONE . '][0][title]' => $image_info['title'], 850 ); 851 $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save')); 852 $default_output = theme('image', $image_info); 853 $this->assertRaw($default_output, t('Image displayed using user supplied alt and title attributes.')); 854 855 // Verify that alt/title longer than allowed results in a validation error. 856 $test_size = 2000; 857 $edit = array( 858 $field_name . '[' . LANGUAGE_NONE . '][0][alt]' => $this->randomName($test_size), 859 $field_name . '[' . LANGUAGE_NONE . '][0][title]' => $this->randomName($test_size), 860 ); 861 $this->drupalPost('node/' . $nid . '/edit', $edit, t('Save')); 862 $this->assertRaw(t('Alternate text cannot be longer than %max characters but is currently %length characters long.', array( 863 '%max' => $schema['fields'][$field_name .'_alt']['length'], 864 '%length' => $test_size, 865 ))); 866 $this->assertRaw(t('Title cannot be longer than %max characters but is currently %length characters long.', array( 867 '%max' => $schema['fields'][$field_name .'_title']['length'], 868 '%length' => $test_size, 869 ))); 870 } 871 872 /** 873 * Test passing attributes into the image field formatters. 874 */ 875 function testImageFieldFormatterAttributes() { 876 $image = theme('image_formatter', array( 877 'item' => array( 878 'uri' => 'http://example.com/example.png', 879 'attributes' => array( 880 'data-image-field-formatter' => 'testFound', 881 ), 882 'alt' => t('Image field formatter attribute test.'), 883 'title' => t('Image field formatter'), 884 ), 885 )); 886 $this->assertTrue(stripos($image, 'testFound') > 0, t('Image field formatters can have attributes.')); 887 } 888 889 /** 890 * Test use of a default image with an image field. 891 */ 892 function testImageFieldDefaultImage() { 893 // Create a new image field. 894 $field_name = strtolower($this->randomName()); 895 $this->createImageField($field_name, 'article'); 896 897 // Create a new node, with no images and verify that no images are 898 // displayed. 899 $node = $this->drupalCreateNode(array('type' => 'article')); 900 $this->drupalGet('node/' . $node->nid); 901 // Verify that no image is displayed on the page by checking for the class 902 // that would be used on the image field. 903 $this->assertNoPattern('<div class="(.*?)field-name-' . strtr($field_name, '_', '-') . '(.*?)">', t('No image displayed when no image is attached and no default image specified.')); 904 905 // Add a default image to the public imagefield instance. 906 $images = $this->drupalGetTestFiles('image'); 907 $edit = array( 908 'files[field_settings_default_image]' => drupal_realpath($images[0]->uri), 909 ); 910 $this->drupalPost('admin/structure/types/manage/article/fields/' . $field_name, $edit, t('Save settings')); 911 // Clear field info cache so the new default image is detected. 912 field_info_cache_clear(); 913 $field = field_info_field($field_name); 914 $image = file_load($field['settings']['default_image']); 915 $this->assertTrue($image->status == FILE_STATUS_PERMANENT, t('The default image status is permanent.')); 916 $default_output = theme('image', array('path' => $image->uri)); 917 $this->drupalGet('node/' . $node->nid); 918 $this->assertRaw($default_output, t('Default image displayed when no user supplied image is present.')); 919 920 // Create a node with an image attached and ensure that the default image 921 // is not displayed. 922 $nid = $this->uploadNodeImage($images[1], $field_name, 'article'); 923 $node = node_load($nid, NULL, TRUE); 924 $image_info = array( 925 'path' => $node->{$field_name}[LANGUAGE_NONE][0]['uri'], 926 'width' => 40, 927 'height' => 20, 928 ); 929 $image_output = theme('image', $image_info); 930 $this->drupalGet('node/' . $nid); 931 $this->assertNoRaw($default_output, t('Default image is not displayed when user supplied image is present.')); 932 $this->assertRaw($image_output, t('User supplied image is displayed.')); 933 934 // Remove default image from the field and make sure it is no longer used. 935 $edit = array( 936 'field[settings][default_image][fid]' => 0, 937 ); 938 $this->drupalPost('admin/structure/types/manage/article/fields/' . $field_name, $edit, t('Save settings')); 939 // Clear field info cache so the new default image is detected. 940 field_info_cache_clear(); 941 $field = field_info_field($field_name); 942 $this->assertFalse($field['settings']['default_image'], t('Default image removed from field.')); 943 // Create an image field that uses the private:// scheme and test that the 944 // default image works as expected. 945 $private_field_name = strtolower($this->randomName()); 946 $this->createImageField($private_field_name, 'article', array('uri_scheme' => 'private')); 947 // Add a default image to the new field. 948 $edit = array( 949 'files[field_settings_default_image]' => drupal_realpath($images[1]->uri), 950 ); 951 $this->drupalPost('admin/structure/types/manage/article/fields/' . $private_field_name, $edit, t('Save settings')); 952 $private_field = field_info_field($private_field_name); 953 $image = file_load($private_field['settings']['default_image']); 954 $this->assertEqual('private', file_uri_scheme($image->uri), t('Default image uses private:// scheme.')); 955 $this->assertTrue($image->status == FILE_STATUS_PERMANENT, t('The default image status is permanent.')); 956 // Create a new node with no image attached and ensure that default private 957 // image is displayed. 958 $node = $this->drupalCreateNode(array('type' => 'article')); 959 $default_output = theme('image', array('path' => $image->uri)); 960 $this->drupalGet('node/' . $node->nid); 961 $this->assertRaw($default_output, t('Default private image displayed when no user supplied image is present.')); 962 } 963 } 964 965 /** 966 * Test class to check for various validations. 967 */ 968 class ImageFieldValidateTestCase extends ImageFieldTestCase { 969 public static function getInfo() { 970 return array( 971 'name' => 'Image field validation tests', 972 'description' => 'Tests validation functions such as min/max resolution.', 973 'group' => 'Image', 974 ); 975 } 976 977 /** 978 * Test min/max resolution settings. 979 */ 980 function testResolution() { 981 $field_name = strtolower($this->randomName()); 982 $min_resolution = 50; 983 $max_resolution = 100; 984 $instance_settings = array( 985 'max_resolution' => $max_resolution . 'x' . $max_resolution, 986 'min_resolution' => $min_resolution . 'x' . $min_resolution, 987 ); 988 $this->createImageField($field_name, 'article', array(), $instance_settings); 989 990 // We want a test image that is too small, and a test image that is too 991 // big, so cycle through test image files until we have what we need. 992 $image_that_is_too_big = FALSE; 993 $image_that_is_too_small = FALSE; 994 foreach ($this->drupalGetTestFiles('image') as $image) { 995 $info = image_get_info($image->uri); 996 if ($info['width'] > $max_resolution) { 997 $image_that_is_too_big = $image; 998 } 999 if ($info['width'] < $min_resolution) { 1000 $image_that_is_too_small = $image; 1001 } 1002 if ($image_that_is_too_small && $image_that_is_too_big) { 1003 break; 1004 } 1005 } 1006 $nid = $this->uploadNodeImage($image_that_is_too_small, $field_name, 'article'); 1007 $this->assertText(t('The specified file ' . $image_that_is_too_small->filename . ' could not be uploaded. The image is too small; the minimum dimensions are 50x50 pixels.'), t('Node save failed when minimum image resolution was not met.')); 1008 $nid = $this->uploadNodeImage($image_that_is_too_big, $field_name, 'article'); 1009 $this->assertText(t('The image was resized to fit within the maximum allowed dimensions of 100x100 pixels.'), t('Image exceeding max resolution was properly resized.')); 1010 } 1011 } 1012 1013 /** 1014 * Tests that images have correct dimensions when styled. 1015 */ 1016 class ImageDimensionsTestCase extends DrupalWebTestCase { 1017 1018 public static function getInfo() { 1019 return array( 1020 'name' => 'Image dimensions', 1021 'description' => 'Tests that images have correct dimensions when styled.', 1022 'group' => 'Image', 1023 ); 1024 } 1025 1026 function setUp() { 1027 parent::setUp('image_module_test'); 1028 } 1029 1030 /** 1031 * Test styled image dimensions cumulatively. 1032 */ 1033 function testImageDimensions() { 1034 // Create a working copy of the file. 1035 $files = $this->drupalGetTestFiles('image'); 1036 $file = reset($files); 1037 $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); 1038 1039 // Create a style. 1040 $style = image_style_save(array('name' => 'test')); 1041 $generated_uri = 'public://styles/test/public/'. drupal_basename($original_uri); 1042 $url = image_style_url('test', $original_uri); 1043 1044 $variables = array( 1045 'style_name' => 'test', 1046 'path' => $original_uri, 1047 'width' => 40, 1048 'height' => 20, 1049 ); 1050 1051 // Scale an image that is wider than it is high. 1052 $effect = array( 1053 'name' => 'image_scale', 1054 'data' => array( 1055 'width' => 120, 1056 'height' => 90, 1057 'upscale' => TRUE, 1058 ), 1059 'isid' => $style['isid'], 1060 ); 1061 1062 image_effect_save($effect); 1063 $img_tag = theme_image_style($variables); 1064 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" width="120" height="60" alt="" />', t('Expected img tag was found.')); 1065 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 1066 $this->drupalGet($url); 1067 $this->assertResponse(200, t('Image was generated at the URL.')); 1068 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 1069 $image_info = image_get_info($generated_uri); 1070 $this->assertEqual($image_info['width'], 120, t('Expected width was found.')); 1071 $this->assertEqual($image_info['height'], 60, t('Expected height was found.')); 1072 1073 // Rotate 90 degrees anticlockwise. 1074 $effect = array( 1075 'name' => 'image_rotate', 1076 'data' => array( 1077 'degrees' => -90, 1078 'random' => FALSE, 1079 ), 1080 'isid' => $style['isid'], 1081 ); 1082 1083 image_effect_save($effect); 1084 $img_tag = theme_image_style($variables); 1085 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" width="60" height="120" alt="" />', t('Expected img tag was found.')); 1086 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 1087 $this->drupalGet($url); 1088 $this->assertResponse(200, t('Image was generated at the URL.')); 1089 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 1090 $image_info = image_get_info($generated_uri); 1091 $this->assertEqual($image_info['width'], 60, t('Expected width was found.')); 1092 $this->assertEqual($image_info['height'], 120, t('Expected height was found.')); 1093 1094 // Scale an image that is higher than it is wide (rotated by previous effect). 1095 $effect = array( 1096 'name' => 'image_scale', 1097 'data' => array( 1098 'width' => 120, 1099 'height' => 90, 1100 'upscale' => TRUE, 1101 ), 1102 'isid' => $style['isid'], 1103 ); 1104 1105 image_effect_save($effect); 1106 $img_tag = theme_image_style($variables); 1107 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" width="45" height="90" alt="" />', t('Expected img tag was found.')); 1108 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 1109 $this->drupalGet($url); 1110 $this->assertResponse(200, t('Image was generated at the URL.')); 1111 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 1112 $image_info = image_get_info($generated_uri); 1113 $this->assertEqual($image_info['width'], 45, t('Expected width was found.')); 1114 $this->assertEqual($image_info['height'], 90, t('Expected height was found.')); 1115 1116 // Test upscale disabled. 1117 $effect = array( 1118 'name' => 'image_scale', 1119 'data' => array( 1120 'width' => 400, 1121 'height' => 200, 1122 'upscale' => FALSE, 1123 ), 1124 'isid' => $style['isid'], 1125 ); 1126 1127 image_effect_save($effect); 1128 $img_tag = theme_image_style($variables); 1129 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" width="45" height="90" alt="" />', t('Expected img tag was found.')); 1130 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 1131 $this->drupalGet($url); 1132 $this->assertResponse(200, t('Image was generated at the URL.')); 1133 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 1134 $image_info = image_get_info($generated_uri); 1135 $this->assertEqual($image_info['width'], 45, t('Expected width was found.')); 1136 $this->assertEqual($image_info['height'], 90, t('Expected height was found.')); 1137 1138 // Add a desaturate effect. 1139 $effect = array( 1140 'name' => 'image_desaturate', 1141 'data' => array(), 1142 'isid' => $style['isid'], 1143 ); 1144 1145 image_effect_save($effect); 1146 $img_tag = theme_image_style($variables); 1147 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" width="45" height="90" alt="" />', t('Expected img tag was found.')); 1148 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 1149 $this->drupalGet($url); 1150 $this->assertResponse(200, t('Image was generated at the URL.')); 1151 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 1152 $image_info = image_get_info($generated_uri); 1153 $this->assertEqual($image_info['width'], 45, t('Expected width was found.')); 1154 $this->assertEqual($image_info['height'], 90, t('Expected height was found.')); 1155 1156 // Add a random rotate effect. 1157 $effect = array( 1158 'name' => 'image_rotate', 1159 'data' => array( 1160 'degrees' => 180, 1161 'random' => TRUE, 1162 ), 1163 'isid' => $style['isid'], 1164 ); 1165 1166 image_effect_save($effect); 1167 $img_tag = theme_image_style($variables); 1168 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" alt="" />', t('Expected img tag was found.')); 1169 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 1170 $this->drupalGet($url); 1171 $this->assertResponse(200, t('Image was generated at the URL.')); 1172 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 1173 1174 1175 // Add a crop effect. 1176 $effect = array( 1177 'name' => 'image_crop', 1178 'data' => array( 1179 'width' => 30, 1180 'height' => 30, 1181 'anchor' => 'center-center', 1182 ), 1183 'isid' => $style['isid'], 1184 ); 1185 1186 image_effect_save($effect); 1187 $img_tag = theme_image_style($variables); 1188 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" width="30" height="30" alt="" />', t('Expected img tag was found.')); 1189 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 1190 $this->drupalGet($url); 1191 $this->assertResponse(200, t('Image was generated at the URL.')); 1192 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 1193 $image_info = image_get_info($generated_uri); 1194 $this->assertEqual($image_info['width'], 30, t('Expected width was found.')); 1195 $this->assertEqual($image_info['height'], 30, t('Expected height was found.')); 1196 1197 // Rotate to a non-multiple of 90 degrees. 1198 $effect = array( 1199 'name' => 'image_rotate', 1200 'data' => array( 1201 'degrees' => 57, 1202 'random' => FALSE, 1203 ), 1204 'isid' => $style['isid'], 1205 ); 1206 1207 $effect = image_effect_save($effect); 1208 $img_tag = theme_image_style($variables); 1209 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" alt="" />', t('Expected img tag was found.')); 1210 $this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.')); 1211 $this->drupalGet($url); 1212 $this->assertResponse(200, t('Image was generated at the URL.')); 1213 $this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.')); 1214 1215 image_effect_delete($effect); 1216 1217 // Ensure that an effect with no dimensions callback unsets the dimensions. 1218 // This ensures compatibility with 7.0 contrib modules. 1219 $effect = array( 1220 'name' => 'image_module_test_null', 1221 'data' => array(), 1222 'isid' => $style['isid'], 1223 ); 1224 1225 image_effect_save($effect); 1226 $img_tag = theme_image_style($variables); 1227 $this->assertEqual($img_tag, '<img typeof="foaf:Image" src="' . $url . '" alt="" />', t('Expected img tag was found.')); 1228 } 1229 } 1230 1231 /** 1232 * Tests image_dimensions_scale(). 1233 */ 1234 class ImageDimensionsScaleTestCase extends DrupalUnitTestCase { 1235 public static function getInfo() { 1236 return array( 1237 'name' => 'image_dimensions_scale()', 1238 'description' => 'Tests all control flow branches in image_dimensions_scale().', 1239 'group' => 'Image', 1240 ); 1241 } 1242 1243 /** 1244 * Tests all control flow branches in image_dimensions_scale(). 1245 */ 1246 function testImageDimensionsScale() { 1247 // Define input / output datasets to test different branch conditions. 1248 $test = array(); 1249 1250 // Test branch conditions: 1251 // - No height. 1252 // - Upscale, don't need to upscale. 1253 $tests[] = array( 1254 'input' => array( 1255 'dimensions' => array( 1256 'width' => 1000, 1257 'height' => 2000, 1258 ), 1259 'width' => 200, 1260 'height' => NULL, 1261 'upscale' => TRUE, 1262 ), 1263 'output' => array( 1264 'dimensions' => array( 1265 'width' => 200, 1266 'height' => 400, 1267 ), 1268 'return_value' => TRUE, 1269 ), 1270 ); 1271 1272 // Test branch conditions: 1273 // - No width. 1274 // - Don't upscale, don't need to upscale. 1275 $tests[] = array( 1276 'input' => array( 1277 'dimensions' => array( 1278 'width' => 1000, 1279 'height' => 800, 1280 ), 1281 'width' => NULL, 1282 'height' => 140, 1283 'upscale' => FALSE, 1284 ), 1285 'output' => array( 1286 'dimensions' => array( 1287 'width' => 175, 1288 'height' => 140, 1289 ), 1290 'return_value' => TRUE, 1291 ), 1292 ); 1293 1294 // Test branch conditions: 1295 // - Source aspect ratio greater than target. 1296 // - Upscale, need to upscale. 1297 $tests[] = array( 1298 'input' => array( 1299 'dimensions' => array( 1300 'width' => 8, 1301 'height' => 20, 1302 ), 1303 'width' => 200, 1304 'height' => 140, 1305 'upscale' => TRUE, 1306 ), 1307 'output' => array( 1308 'dimensions' => array( 1309 'width' => 56, 1310 'height' => 140, 1311 ), 1312 'return_value' => TRUE, 1313 ), 1314 ); 1315 1316 // Test branch condition: target aspect ratio greater than source. 1317 $tests[] = array( 1318 'input' => array( 1319 'dimensions' => array( 1320 'width' => 2000, 1321 'height' => 800, 1322 ), 1323 'width' => 200, 1324 'height' => 140, 1325 'upscale' => FALSE, 1326 ), 1327 'output' => array( 1328 'dimensions' => array( 1329 'width' => 200, 1330 'height' => 80, 1331 ), 1332 'return_value' => TRUE, 1333 ), 1334 ); 1335 1336 // Test branch condition: don't upscale, need to upscale. 1337 $tests[] = array( 1338 'input' => array( 1339 'dimensions' => array( 1340 'width' => 100, 1341 'height' => 50, 1342 ), 1343 'width' => 200, 1344 'height' => 140, 1345 'upscale' => FALSE, 1346 ), 1347 'output' => array( 1348 'dimensions' => array( 1349 'width' => 100, 1350 'height' => 50, 1351 ), 1352 'return_value' => FALSE, 1353 ), 1354 ); 1355 1356 foreach ($tests as $test) { 1357 // Process the test dataset. 1358 $return_value = image_dimensions_scale($test['input']['dimensions'], $test['input']['width'], $test['input']['height'], $test['input']['upscale']); 1359 1360 // Check the width. 1361 $this->assertEqual($test['output']['dimensions']['width'], $test['input']['dimensions']['width'], t('Computed width (@computed_width) equals expected width (@expected_width)', array('@computed_width' => $test['output']['dimensions']['width'], '@expected_width' => $test['input']['dimensions']['width']))); 1362 1363 // Check the height. 1364 $this->assertEqual($test['output']['dimensions']['height'], $test['input']['dimensions']['height'], t('Computed height (@computed_height) equals expected height (@expected_height)', array('@computed_height' => $test['output']['dimensions']['height'], '@expected_height' => $test['input']['dimensions']['height']))); 1365 1366 // Check the return value. 1367 $this->assertEqual($test['output']['return_value'], $return_value, t('Correct return value.')); 1368 } 1369 } 1370 } 1371 1372 /** 1373 * Tests default image settings. 1374 */ 1375 class ImageFieldDefaultImagesTestCase extends ImageFieldTestCase { 1376 1377 public static function getInfo() { 1378 return array( 1379 'name' => 'Image field default images tests', 1380 'description' => 'Tests setting up default images both to the field and field instance.', 1381 'group' => 'Image', 1382 ); 1383 } 1384 1385 function setUp() { 1386 parent::setUp(array('field_ui')); 1387 } 1388 1389 /** 1390 * Tests CRUD for fields and fields instances with default images. 1391 */ 1392 function testDefaultImages() { 1393 // Create files to use as the default images. 1394 $files = $this->drupalGetTestFiles('image'); 1395 $default_images = array(); 1396 foreach (array('field', 'instance', 'instance2', 'field_new', 'instance_new') as $image_target) { 1397 $file = array_pop($files); 1398 $file = file_save($file); 1399 $default_images[$image_target] = $file; 1400 } 1401 1402 // Create an image field and add an instance to the article content type. 1403 $field_name = strtolower($this->randomName()); 1404 $field_settings = array( 1405 'default_image' => $default_images['field']->fid, 1406 ); 1407 $instance_settings = array( 1408 'default_image' => $default_images['instance']->fid, 1409 ); 1410 $widget_settings = array( 1411 'preview_image_style' => 'medium', 1412 ); 1413 $this->createImageField($field_name, 'article', $field_settings, $instance_settings, $widget_settings); 1414 $field = field_info_field($field_name); 1415 $instance = field_info_instance('node', $field_name, 'article'); 1416 1417 // Add another instance with another default image to the page content type. 1418 $instance2 = array_merge($instance, array( 1419 'bundle' => 'page', 1420 'settings' => array( 1421 'default_image' => $default_images['instance2']->fid, 1422 ), 1423 )); 1424 field_create_instance($instance2); 1425 $instance2 = field_info_instance('node', $field_name, 'page'); 1426 1427 1428 // Confirm the defaults are present on the article field admin form. 1429 $this->drupalGet("admin/structure/types/manage/article/fields/$field_name"); 1430 $this->assertFieldByXpath( 1431 '//input[@name="field[settings][default_image][fid]"]', 1432 $default_images['field']->fid, 1433 format_string( 1434 'Article image field default equals expected file ID of @fid.', 1435 array('@fid' => $default_images['field']->fid) 1436 ) 1437 ); 1438 $this->assertFieldByXpath( 1439 '//input[@name="instance[settings][default_image][fid]"]', 1440 $default_images['instance']->fid, 1441 format_string( 1442 'Article image field instance default equals expected file ID of @fid.', 1443 array('@fid' => $default_images['instance']->fid) 1444 ) 1445 ); 1446 1447 // Confirm the defaults are present on the page field admin form. 1448 $this->drupalGet("admin/structure/types/manage/page/fields/$field_name"); 1449 $this->assertFieldByXpath( 1450 '//input[@name="field[settings][default_image][fid]"]', 1451 $default_images['field']->fid, 1452 format_string( 1453 'Page image field default equals expected file ID of @fid.', 1454 array('@fid' => $default_images['field']->fid) 1455 ) 1456 ); 1457 $this->assertFieldByXpath( 1458 '//input[@name="instance[settings][default_image][fid]"]', 1459 $default_images['instance2']->fid, 1460 format_string( 1461 'Page image field instance default equals expected file ID of @fid.', 1462 array('@fid' => $default_images['instance2']->fid) 1463 ) 1464 ); 1465 1466 // Confirm that the image default is shown for a new article node. 1467 $article = $this->drupalCreateNode(array('type' => 'article')); 1468 $article_built = node_view($article); 1469 $this->assertEqual( 1470 $article_built[$field_name]['#items'][0]['fid'], 1471 $default_images['instance']->fid, 1472 format_string( 1473 'A new article node without an image has the expected default image file ID of @fid.', 1474 array('@fid' => $default_images['instance']->fid) 1475 ) 1476 ); 1477 1478 // Confirm that the image default is shown for a new page node. 1479 $page = $this->drupalCreateNode(array('type' => 'page')); 1480 $page_built = node_view($page); 1481 $this->assertEqual( 1482 $page_built[$field_name]['#items'][0]['fid'], 1483 $default_images['instance2']->fid, 1484 format_string( 1485 'A new page node without an image has the expected default image file ID of @fid.', 1486 array('@fid' => $default_images['instance2']->fid) 1487 ) 1488 ); 1489 1490 // Upload a new default for the field. 1491 $field['settings']['default_image'] = $default_images['field_new']->fid; 1492 field_update_field($field); 1493 1494 // Confirm that the new field default is used on the article admin form. 1495 $this->drupalGet("admin/structure/types/manage/article/fields/$field_name"); 1496 $this->assertFieldByXpath( 1497 '//input[@name="field[settings][default_image][fid]"]', 1498 $default_images['field_new']->fid, 1499 format_string( 1500 'Updated image field default equals expected file ID of @fid.', 1501 array('@fid' => $default_images['field_new']->fid) 1502 ) 1503 ); 1504 1505 // Reload the nodes and confirm the field instance defaults are used. 1506 $article_built = node_view($article = node_load($article->nid, NULL, $reset = TRUE)); 1507 $page_built = node_view($page = node_load($page->nid, NULL, $reset = TRUE)); 1508 $this->assertEqual( 1509 $article_built[$field_name]['#items'][0]['fid'], 1510 $default_images['instance']->fid, 1511 format_string( 1512 'An existing article node without an image has the expected default image file ID of @fid.', 1513 array('@fid' => $default_images['instance']->fid) 1514 ) 1515 ); 1516 $this->assertEqual( 1517 $page_built[$field_name]['#items'][0]['fid'], 1518 $default_images['instance2']->fid, 1519 format_string( 1520 'An existing page node without an image has the expected default image file ID of @fid.', 1521 array('@fid' => $default_images['instance2']->fid) 1522 ) 1523 ); 1524 1525 // Upload a new default for the article's field instance. 1526 $instance['settings']['default_image'] = $default_images['instance_new']->fid; 1527 field_update_instance($instance); 1528 1529 // Confirm the new field instance default is used on the article field 1530 // admin form. 1531 $this->drupalGet("admin/structure/types/manage/article/fields/$field_name"); 1532 $this->assertFieldByXpath( 1533 '//input[@name="instance[settings][default_image][fid]"]', 1534 $default_images['instance_new']->fid, 1535 format_string( 1536 'Updated article image field instance default equals expected file ID of @fid.', 1537 array('@fid' => $default_images['instance_new']->fid) 1538 ) 1539 ); 1540 1541 // Reload the nodes. 1542 $article_built = node_view($article = node_load($article->nid, NULL, $reset = TRUE)); 1543 $page_built = node_view($page = node_load($page->nid, NULL, $reset = TRUE)); 1544 1545 // Confirm the article uses the new default. 1546 $this->assertEqual( 1547 $article_built[$field_name]['#items'][0]['fid'], 1548 $default_images['instance_new']->fid, 1549 format_string( 1550 'An existing article node without an image has the expected default image file ID of @fid.', 1551 array('@fid' => $default_images['instance_new']->fid) 1552 ) 1553 ); 1554 // Confirm the page remains unchanged. 1555 $this->assertEqual( 1556 $page_built[$field_name]['#items'][0]['fid'], 1557 $default_images['instance2']->fid, 1558 format_string( 1559 'An existing page node without an image has the expected default image file ID of @fid.', 1560 array('@fid' => $default_images['instance2']->fid) 1561 ) 1562 ); 1563 1564 // Remove the instance default from articles. 1565 $instance['settings']['default_image'] = NULL; 1566 field_update_instance($instance); 1567 1568 // Confirm the article field instance default has been removed. 1569 $this->drupalGet("admin/structure/types/manage/article/fields/$field_name"); 1570 $this->assertFieldByXpath( 1571 '//input[@name="instance[settings][default_image][fid]"]', 1572 '', 1573 'Updated article image field instance default has been successfully removed.' 1574 ); 1575 1576 // Reload the nodes. 1577 $article_built = node_view($article = node_load($article->nid, NULL, $reset = TRUE)); 1578 $page_built = node_view($page = node_load($page->nid, NULL, $reset = TRUE)); 1579 // Confirm the article uses the new field (not instance) default. 1580 $this->assertEqual( 1581 $article_built[$field_name]['#items'][0]['fid'], 1582 $default_images['field_new']->fid, 1583 format_string( 1584 'An existing article node without an image has the expected default image file ID of @fid.', 1585 array('@fid' => $default_images['field_new']->fid) 1586 ) 1587 ); 1588 // Confirm the page remains unchanged. 1589 $this->assertEqual( 1590 $page_built[$field_name]['#items'][0]['fid'], 1591 $default_images['instance2']->fid, 1592 format_string( 1593 'An existing page node without an image has the expected default image file ID of @fid.', 1594 array('@fid' => $default_images['instance2']->fid) 1595 ) 1596 ); 1597 } 1598 1599 } 1600 1601 /** 1602 * Tests image theme functions. 1603 */ 1604 class ImageThemeFunctionWebTestCase extends DrupalWebTestCase { 1605 1606 public static function getInfo() { 1607 return array( 1608 'name' => 'Image theme functions', 1609 'description' => 'Test that the image theme functions work correctly.', 1610 'group' => 'Image', 1611 ); 1612 } 1613 1614 function setUp() { 1615 parent::setUp(array('image')); 1616 } 1617 1618 /** 1619 * Tests usage of the image field formatters. 1620 */ 1621 function testImageFormatterTheme() { 1622 // Create an image. 1623 $files = $this->drupalGetTestFiles('image'); 1624 $file = reset($files); 1625 $original_uri = file_unmanaged_copy($file->uri, 'public://', FILE_EXISTS_RENAME); 1626 1627 // Create a style. 1628 image_style_save(array('name' => 'test')); 1629 $url = image_style_url('test', $original_uri); 1630 1631 // Test using theme_image_formatter() without an image title, alt text, or 1632 // link options. 1633 $path = $this->randomName(); 1634 $element = array( 1635 '#theme' => 'image_formatter', 1636 '#image_style' => 'test', 1637 '#item' => array( 1638 'uri' => $original_uri, 1639 ), 1640 '#path' => array( 1641 'path' => $path, 1642 ), 1643 ); 1644 $rendered_element = render($element); 1645 $expected_result = '<a href="' . url($path) . '"><img typeof="foaf:Image" src="' . $url . '" alt="" /></a>'; 1646 $this->assertEqual($expected_result, $rendered_element, 'theme_image_formatter() correctly renders without title, alt, or path options.'); 1647 1648 // Link the image to a fragment on the page, and not a full URL. 1649 $fragment = $this->randomName(); 1650 $element['#path']['path'] = ''; 1651 $element['#path']['options'] = array( 1652 'external' => TRUE, 1653 'fragment' => $fragment, 1654 ); 1655 $rendered_element = render($element); 1656 $expected_result = '<a href="#' . $fragment . '"><img typeof="foaf:Image" src="' . $url . '" alt="" /></a>'; 1657 $this->assertEqual($expected_result, $rendered_element, 'theme_image_formatter() correctly renders a link fragment.'); 1658 } 1659 1660 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title