<?php
// $Id: image.test,v 1.14 2009/12/30 12:51:47 joachim Exp $

/**
 * Test image functionality.
 */
class ImageTestCase extends DrupalWebTestCase {
  protected $web_user;
  protected $image;
  protected $another_image;

  function getInfo() {
    return array(
      'name' => t('Image functionality'),
      'description' => t('Test Image module functionality.'),
      'group' => t('Image'),
    );
  }

  function setUp() {
    parent::setUp('image');

    // User to create images.
    $this->web_user = $this->drupalCreateUser(array('create images', 'view original images', 'edit own images', 'edit any images',
      'delete own images', 'delete any images', 'administer site configuration'));    
    $this->drupalLogin($this->web_user);

    // Uploadable image.
    $this->image = 'misc/druplicon.png';
    $this->another_image = 'misc/throbber.gif';

    // Set small dimensions for testing scale so $this->image is small enough.
    $image_sizes = image_get_sizes();
    $image_sizes['_original']['operation'] = 'scale';
    $image_sizes['thumbnail']['operation'] = 'scale';
    $image_sizes['thumbnail']['width'] = 5;
    $image_sizes['thumbnail']['height'] = 5;
    $image_sizes['preview']['operation'] = 'scale';
    $image_sizes['preview']['width'] = 10;
    $image_sizes['preview']['height'] = 10;
    variable_set('image_sizes', $image_sizes);
  }

  /**
   * Clean-up temporary files.
   *
   * @see system_cron()
   *
   * @todo This lets SimpleTest die for any reason.
   */
  function __tearDown() {
    $result = db_query('SELECT * FROM {files} WHERE status = %d', FILE_STATUS_TEMPORARY);
    while ($file = db_fetch_object($result)) {
      if (file_exists($file->filepath)) {
        // If files that exist cannot be deleted, continue so the database remains
        // consistent.
        if (!file_delete($file->filepath)) {
          $this->error(t('Could not delete temporary file "%path" during garbage collection', array('%path' => $file->filepath)));
          continue;
        }
      }
      db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
    }
  }

  /**
   * Verify creating/displaying/editing/deleting image nodes.
   */
  function testImageNode() {
    // Create an image.
    $edit = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
      'files[image]' => realpath($this->image),
    );
    $this->drupalPost('node/add/image', $edit, t('Save'));

    $this->assertRaw(t('@type %title has been created.', array('@type' => 'Image', '%title' => $edit['title'])), t('Image node was created.'));

    $node = node_load(array('title' => $edit['title']));
    $this->assertTrue($node, t('Image node is found in database.'));

    // Display an image.
    $this->drupalGet('node/' . $node->nid, array('query' => 'size=_original'));
    $this->assertPattern('@<img[^>]+?' . $node->images['_original'] . '[^>]+?>@', t('Original image displayed on the page.'));
    $this->assertTrue(file_exists($node->images['_original']), t('Original image exists.'));

    $this->drupalGet('node/' . $node->nid);
    $this->assertPattern('@<img[^>]+?' . $node->images['preview'] . '[^>]+?>@', t('Image preview displayed on the page.'));
    $this->assertTrue(file_exists($node->images['preview']), t('Image preview exists.'));

    $this->drupalGet('node/' . $node->nid, array('query' => 'size=thumbnail'));
    $this->assertPattern('@<img[^>]+?' . $node->images['thumbnail'] . '[^>]+?>@', t('Image thumbnail displayed on the page.'));
    $this->assertTrue(file_exists($node->images['thumbnail']), t('Image thumbnail exists.'));

    // Promote the node so we can test the thumbnail appears in the teaser.
    $node->promote = TRUE;
    node_save($node);
    $this->drupalGet('node');
    $this->assertPattern('@<img[^>]+?' . $node->images['thumbnail'] . '[^>]+?>@', t('Image thumbnail displayed on the front page teaser.'));    
    
    // Edit an image.
    $another_edit = array(
      'title' => $edit['title'],
      'files[image]' => realpath($this->another_image),
    );
    $this->drupalPost('node/' . $node->nid . '/edit', $another_edit, t('Save'));
    $another_node = node_load(array('title' => $edit['title']));
    $this->assertFalse(file_exists($node->images['preview']) || file_exists($node->images['_original']) || file_exists($node->images['thumbnail']), t('Previous image derivative files were deleted.'));

    // Delete an image.
    $this->drupalPost('node/' . $node->nid . '/delete', array(), t('Delete'));
    $this->assertRaw(t('@type %title has been deleted.', array('@type' => 'Image', '%title' => $edit['title'])), t('Image node was deleted.'));
    $node = node_load(array('title' => $edit['title']));
    $this->assertFalse($node, t('Image node is not found in database.'));
    $this->assertFalse(file_exists($another_node->images['preview']) || file_exists($another_node->images['_original']) || file_exists($another_node->images['thumbnail']), t('Image file was deleted.'));
  }

  /**
   * Verify that images cannot be created without a file.
   */
  function testImageNodeValidation() {
    // Create an image node without image file.
    $edit = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
    );
    $this->drupalPost('node/add/image', $edit, t('Save'));
    $this->assertRaw(t('You must upload an image.'), t('Refused node creation without image.'));

    $node = node_load(array('title' => $edit['title']));
    $this->assertFalse($node, t('Image node is not found in database.'));
  }

  /**
   * Verify image previews and proper submission.
   */
  function testImageNodePreview() {
    // Setup POST data for image.
    $edit = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
      'files[image]' => realpath($this->image),
    );

    // Preview image node.
    $this->drupalGet('node/add/image');
    $this->drupalPost(NULL, $edit, t('Preview'));

    // Save image node preview as is.
    $this->drupalPost(NULL, NULL, t('Save'));

    // Verify image node was properly created.
    $node = node_load(array('title' => $edit['title']));
    $this->assertTrue($node, t('Image node is found in database.'));
    $this->assertTrue(file_exists($node->images['_original']), t('Original image exists.'));
    $filename = preg_replace('/_[^\.]+/', '', basename($node->images['_original']));
    $this->assertTrue($filename == basename($this->image), t('Image file was properly stored.'));
  }

  /**
   * Verify that images can be created in parallel (session handling).
   *
   * @todo Doesn't look valid. Can SimpleTest support this at all?
   * @todo testImageCreateNodeFrom() fails is this test is run.
   */
  function __testImageNodeValidationAsync() {
    // Setup POST data for first image.
    $edit1 = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
      'files[image]' => realpath($this->image),
    );
    // Setup POST data for second image.
    $edit2 = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
      'files[image]' => realpath($this->another_image),
    );

    // Preview the first image node.
    $this->drupalGet('node/add/image');
    $this->drupalPost(NULL, $edit1, t('Preview'));

    // Preview the second image node.
    $this->drupalGet('node/add/image');
    $this->drupalPost(NULL, $edit2, t('Preview'));

    // Save the first image node.
    $this->drupalPost(NULL, $edit1, t('Save'));
    // Save the second image node.
    $this->drupalPost('node/add/image', $edit2, t('Save'));

    // Verify first image node contains the first image.
    $node = node_load(array('title' => $edit1['title']));
    $filename = preg_replace('/_[^\.]+/', '', basename($node->images['_original']));
    $this->assertTrue($filename == basename($this->image), t('First image was properly submitted.'));

    // Verify second image node contains the second image.
    $node = node_load(array('title' => $edit2['title']));
    $filename = preg_replace('/_[^\.]+/', '', basename($node->images['_original']));
    $this->assertTrue($filename == basename($this->another_image), t('Second image was properly submitted.'));
  }

  /**
   * Verify derivatives take their path from the original image.
   */
  function testImageDerivativePath() {
    // Create an image.
    $edit = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
      'files[image]' => realpath($this->image),
    );
    $this->drupalPost('node/add/image', $edit, t('Save'));

    $this->assertRaw(t('@type %title has been created.', array('@type' => 'Image', '%title' => $edit['title'])),
      t('Image node was created.'));

    $node = node_load(array('title' => $edit['title']));
    $this->assertTrue($node, t('Image node is found in database.'));

    // Change the file path using admin form.
    $config_change = array(
      'image_default_path' => 'images_alternate',
    );
    $this->drupalPost('admin/settings/image', $config_change, t('Save configuration'));
    $this->assertRaw(t('The configuration options have been saved.'), t('Image path was changed.'));

    // Create another image.
    $second_edit = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
      'files[image]' => realpath($this->another_image),
    );
    $this->drupalPost('node/add/image', $second_edit, t('Save'));

    // Test that image node was created and that it has the correct path.
    $this->assertRaw(t('@type %title has been created.', array('@type' => 'Image', '%title' => $second_edit['title'])),
      t('Second image node was created.'));
    $second_node = node_load(array('title' => $second_edit['title']));
    $this->assertTrue($second_node, t('Second image node is found in database.'));

    $this->assertTrue(preg_match('/images_alternate/', $second_node->images['_original']),
      t("New path {$second_node->images['_original']} used for second image."));

    // Delete physical files for first image's derivatives.
    file_delete($node->images['preview']);
    $this->assertFalse(file_exists($node->images['preview']), t('First image preview image file deleted.'));
    file_delete($node->images['thumbnail']);
    $this->assertFalse(file_exists($node->images['thumbnail']), t('First image thumbnail image file deleted.'));

    // Edit the first image to rebuild derivatives.
    $another_edit = array(
      'rebuild_images' => TRUE,
    );
    $this->drupalPost('node/' . $node->nid . '/edit', $another_edit, t('Save'));
    $this->assertRaw(t('@type %title has been updated.', array('@type' => 'Image', '%title' => $edit['title'])),
      t('First image node was updated to rebuild derivatives.'));

    // Reload first image node.
    $first_node = node_load(array('title' => $edit['title']));

    // Compare regenerated thumbnail paths with original node.
    $this->assertTrue($node->images['thumbnail'] == $first_node->images['thumbnail'], t("Rebuilt derivatives for first image have same thumbnail path as original node: {$node->images['thumbnail']} == regenerated thumbnail path {$first_node->images['thumbnail']}")); 

    // Check regenerated files physically exist.
    $this->assertTrue(file_exists($first_node->images['preview']), t('First image preview file exists.'));
    $this->assertTrue(file_exists($first_node->images['thumbnail']), t('First image thumbnail file exists.'));

    // Change the file path back using admin form.
    $config_change = array(
      'image_default_path' => 'images',
    );
    $this->drupalPost('admin/settings/image', $config_change, t('Save configuration'));
    $this->assertRaw(t('The configuration options have been saved.'), t('Image path was changed back.'));
  } 

  /**
   * Verify that images with missing file information can be deleted from edit form.
   */
  function testImageNodeDeletion() {
    // Create an image.
    $edit = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
      'files[image]' => realpath($this->image),
    );

    // Create image.
    $this->drupalPost('node/add/image', $edit, t('Save'));
    $this->assertRaw(t('@type %title has been created.', array('@type' => 'Image', '%title' => $edit['title'])), t('Image node was created.'));
    $node = node_load(array('title' => $edit['title']));
    $this->assertTrue($node, t('Image node is found in database.'));

    // Remove {files} row to create corruption condition.
    $this->assertTrue(db_query("DELETE f FROM {files} f INNER JOIN {image} i WHERE f.fid = i.fid AND i.nid = %d", $node->nid), t('Image file information was deleted.'));
    $node = node_load(array('title' => $edit['title']));
    $this->assertFalse(isset($node->images['_original']), t('Image file information is missing on re-loaded node.'));

    $this->drupalPost('node/' . $node->nid . '/edit', array(), t('Delete'));
    $this->assertRaw(t('Are you sure you want to delete %title?', array('%title' => $edit['title'])), t('Delete confirmation form is displayed.'));
    $this->drupalPost(NULL, array(), t('Delete'));
    $this->assertRaw(t('@type %title has been deleted.', array('@type' => 'Image', '%title' => $edit['title'])), t('Image node was deleted.'));
  }

  /**
   * Verify image_create_node_from() works like regular image node creation.
   */
  function testImageCreateNodeFrom() {
    $edit = array(
      'title' => $this->randomName(),
      'body' => $this->randomName(),
      'files[image]' => realpath($this->image),
    );
    $this->drupalPost('node/add/image', $edit, t('Save'));
    $this->assertRaw(t('@type %title has been created.', array('@type' => 'Image', '%title' => $edit['title'])), t('Image created.'));

    $node_post = node_load(array('title' => $edit['title']));
    $this->assertTrue($node_post, t('Form created image node found in database.'));

    // Copy the image, since image_create_node_from() deletes the original image.
    file_copy($edit['files[image]'], file_directory_temp());
    $node_api = image_create_node_from(file_directory_temp() . '/' . basename($edit['files[image]']), $edit['title'], $edit['body']);
    $this->assertTrue(is_object($node_api) && $node_api->nid, t('API created image node found in database.'));
    // Rebuild derivative images.
    $node_api = node_load($node_api->nid, NULL, TRUE);

    // Verify that both nodes are equal.
    $equality = ($node_post->title == $node_api->title);
    $this->assertTrue($equality, t('Image node titles are equal.'));
    $equality = $equality && (strip_tags($node_post->body) == strip_tags($node_api->body));
    $this->assertTrue($equality, t('Image node body texts are equal.'));
    $equality = $equality && (filesize($node_post->images['_original']) == filesize($node_api->images['_original']));
    $this->assertTrue($equality, t('Original images are equal.'));
    /*
    // These tests are temporarily commented out as they fail due to 
    // image_create_node_from() not creative derivatives.
    // @see <http://drupal.org/node/670686> for details.
    $equality = $equality && (filesize($node_post->images['preview']) == filesize($node_api->images['preview']));
    $this->assertTrue($equality, t('Preview images are equal.'));
    $equality = $equality && (filesize($node_post->images['thumbnail']) == filesize($node_api->images['thumbnail']));
    $this->assertTrue($equality, t('Preview images are equal.'));
    */
  }
}

