<?php

/**
 * Test the Message CRUD handling.
 */
class MessageCrud extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Message CRUD',
      'description' => 'Test the create, update and remove of Message entities.',
      'group' => 'Message',
    );
  }

  function setUp() {
    parent::setUp('message');
  }

  /**
   * Test CRUD of message entity.
   */
  function testMessageCrud() {
    $web_user = $this->drupalCreateUser();

    $message_type = message_type_create('foo', array('message_text' => array(LANGUAGE_NONE => array(array('value' => 'Example text.')))));
    $message_type->save();

    $message = message_create('foo', array(), $web_user);
    $message->save();
    $mid = $message->mid;

    // Reload the message to see it was saved.
    $message = message_load($mid);
    $this->assertTrue(!empty($message->mid), t('Message was saved to the database.'));

    $this->assertEqual($message->uid, $web_user->uid, 'Message has been saved for the right user.');
    $this->assertEqual($message->getType()->message_text[LANGUAGE_NONE][0]['value'], 'Example text.', 'Message type text has been saved.');

    // Make sure an exception is thrown if message type already exists.
    try {
      $message_type = message_type_create('foo');
      $this->fail("Creating the same message type hasn't created an exception.");
    }
    catch (Exception $e) {
      $this->pass("Exception was thrown: ". $e->getMessage());
    }
  }
}

/**
 * Test the Message CRUD handling.
 */
class MessageShowMessage extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Message view',
      'description' => 'Test viewing a message.',
      'group' => 'Message',
    );
  }

  function setUp() {
    parent::setUp('message');
  }

  /**
   * Test showing a message.
   */
  function testMessageView() {
    // Add language.
    module_enable(array('locale'));
    require_once DRUPAL_ROOT . '/includes/locale.inc';
    for ($i = 0; $i < 2; ++$i) {
      locale_add_language('l' . $i, $this->randomString(), $this->randomString());
    }

    $property = MESSAGE_FIELD_MESSAGE_TEXT;

    // We use randomName instead of randomString since later-on we use
    // strip_tags, so we don't want to get characters that might be escaped.
    $text1 = $this->randomName() . ' argument -- @foo';
    $text2 = $this->randomName() . ' argument -- @foo';
    $message_type = message_type_create('foo');
    $message_type->{$property} = array(
      'en' => array(
        0 => array('value' => 'english text'),
      ),
      'l0' => array(
        0 => array('value' => $text1),
      ),
      'l1' => array(
        0 => array('value' => $text2),
      ),
    );
    $message_type->save();

    // Reload the message type to see it was saved
    $message_type = message_type_load('foo');
    $this->assertTrue(!empty($message_type->id), t('Message type was saved to the database.'));

    // Assert the message type text field exists and is populated.
    $this->assertEqual($message_type->{$property}['l0'][0]['value'], $text1, t('First language message text was saved to the database.'));
    $this->assertEqual($message_type->{$property}['l1'][0]['value'], $text2, t('Second language message text was saved to the database.'));

    $arguments = array('@foo' => $this->randomName(4));
    $message = message_create('foo', array('arguments' => $arguments));
    $message->save();

    // Assert the arguments in the message are replaced.
    $output = $message->getText('l0');
    $this->assertEqual(trim(strip_tags($output)), strtr($text1, $arguments), t('Arguments in the first language message were replaced.'));

    // Assert the arguments in the message are replaced when showing a message
    // from another language.
    $output = $message->getText('l1');
    $this->assertEqual(trim(strip_tags($output)), strtr($text2, $arguments), t('Arguments in the second language message were replaced.'));

    // Assert value is of current language when Locale is enabled and langocde is NULL.
    global $language;
    $output = $message->getText(NULL);
    $this->assertEqual(trim(strip_tags($output)), 'english text', 'Assert value is of current language when Locale is enabled and langocde is NULL.');
  }

  /**
   * Test message-type and message arguments.
   */
  function testMessageArguments() {
    $text = 'test @token1 and @token2';

    $message_type = message_type_create('foo');
    $message_type->{MESSAGE_FIELD_MESSAGE_TEXT} = array(
      LANGUAGE_NONE => array(
        0 => array('value' => $text, 'format' => 'plain_text'),
      ),
    );
    $message_type->arguments = array(
      '@token1' => 'token1',
      '@token2' => 'token2',
    );
    $message_type->save();

    $message = message_create('foo');
    $output = trim(strip_tags($message->getText()));
    $this->assertEqual('test token1 and token2', $output, t('Message type arguments replaced correctly.'));

    // Test overriding message type arguments, with message arguments.
    $message->arguments = array('@token2' => 'token3');
    $output = trim(strip_tags($message->getText()));
    $this->assertEqual('test token1 and token3', $output, t('Message arguments override message type arguments correctly.'));
  }

  /**
   * Test getting only a single delta from the field.
   */
  function testPartials() {
    $text1 = $this->randomName();
    $text2 = $this->randomName();
    $message_type = message_type_create('foo');
    $message_type->{MESSAGE_FIELD_MESSAGE_TEXT} = array(
      LANGUAGE_NONE => array(
        0 => array('value' => $text1, 'format' => 'plain_text'),
        1 => array('value' => $text2, 'format' => 'filtered_html'),
      ),
    );
    $message_type->save();
    $message = message_create('foo');

    $this->assertEqual($text1 . "\n" . $text2 . "\n", strip_tags($message->getText()), t('Got correct text for all deltas.'));

    $options = array(
      'partials' => TRUE,
      'partial delta' => 0,
    );
    $this->assertEqual($text1 . "\n", strip_tags($message->getText(LANGUAGE_NONE, $options)), t('Got correct text for the 1st delta.'));

    $options['partial delta'] = 1;
    $this->assertEqual($text2 . "\n", strip_tags($message->getText(LANGUAGE_NONE, $options)), t('Got correct text for the 2nd delta.'));
  }

  /**
   * Test rendering partials as extra-fields.
   */
  function testExtraField() {
    $message_type = message_type_create('foo');
    $wrapper = entity_metadata_wrapper('message_type', $message_type);
    $wrapper->{MESSAGE_FIELD_MESSAGE_TEXT}[] = array('value' => 'first partial', 'format' => 'plain_text');
    $wrapper->{MESSAGE_FIELD_MESSAGE_TEXT}[] = array('value' => 'second partial', 'format' => 'plain_text');
    $wrapper->{MESSAGE_FIELD_MESSAGE_TEXT}[] = array('value' => 'third partial', 'format' => 'plain_text');
    $wrapper->save();

    $display = field_extra_fields_get_display('message', 'foo', 'default');
    $this->assertEqual(count($display), 3, 'All partials appear in extra-fields.');
    $this->assertTrue(!empty($display['message__message_text__0']) && !empty($display['message__message_text__1']) && !empty($display['message__message_text__2']), 'All partials appear in extra-fields with the correct name.');

    // Check field info cache is cleared on message type save.
    $wrapper->{MESSAGE_FIELD_MESSAGE_TEXT}[] = array('value' => 'fourth partial', 'format' => 'plain_text');
    $wrapper->save();

    $display = field_extra_fields_get_display('message', 'foo', 'default');
    $this->assertEqual(count($display), 4, 'All partials appear in extra-fields, after re-save.');
    $this->assertTrue(!empty($display['message__message_text__0']) && !empty($display['message__message_text__1']) && !empty($display['message__message_text__2']) && !empty($display['message__message_text__3']), 'All partials appear in extra-fields with the correct name, after re-save.');

    $message = message_create('foo');

    // Ensure partials get rendered, before changing any display settings.
    $build = $message->view();
    $text = drupal_render($build);
    $partial1_pos = strpos($text, 'first partial');
    $partial2_pos = strpos($text, 'second partial');
    $partial3_pos = strpos($text, 'third partial');
    $partial4_pos = strpos($text, 'fourth partial');

    $this->assertTrue($partial1_pos && $partial2_pos && $partial3_pos && $partial4_pos, 'All partials found in rendered message text.');

    // Enable the Full view mode, hide the first partial,
    // and display the last partial first.
    $settings = field_bundle_settings('message', 'foo');
    $settings['view_modes']['full']['custom_settings'] = TRUE;
    $settings['extra_fields']['display']['message__message_text__0']['full'] = array('weight' => 0, 'visible' => FALSE);
    $settings['extra_fields']['display']['message__message_text__1']['full'] = array('weight' => 5, 'visible' => TRUE);
    $settings['extra_fields']['display']['message__message_text__2']['full'] = array('weight' => 10, 'visible' => TRUE);
    $settings['extra_fields']['display']['message__message_text__3']['full'] = array('weight' => -5, 'visible' => TRUE);
    field_bundle_settings('message', 'foo', $settings);

    // Render the message text using Full view mode.
    $build = $message->view('full');
    $text = drupal_render($build);
    $partial2_pos = strpos($text, 'second partial');
    $partial3_pos = strpos($text, 'third partial');
    $partial4_pos = strpos($text, 'fourth partial');

    $this->assertNoText('first partial', 'First partial successfully hidden from rendered text using Full view mode.');
    $this->assertTrue($partial2_pos && $partial3_pos && $partial4_pos, 'All partials configured to be visible were rendered.');
    $this->assertTrue(($partial4_pos < $partial2_pos) && ($partial2_pos < $partial3_pos), 'All partials are rendered in the correct order after re-ordering.');

    // Create a Field API text field to render between partials.
    $field = array(
      'field_name' => 'field_bar',
      'type' => 'text',
    );
    field_create_field($field);
    $instance = array(
      'field_name' => 'field_bar',
      'label' => 'Bar',
      'entity_type' => 'message',
      'bundle' => 'foo',
      'settings' => array(),
      'display' => array(
        // Set weight to render between first two partials.
        'full' => array('visible' => TRUE, 'weight' => -3),
      ),
    );
    field_create_instance($instance);

    $message_wrapper = entity_metadata_wrapper('message', $message);
    $message_wrapper->field_bar->set('sample field text');

    $build = $message->view('full');
    $text = drupal_render($build);
    $partial2_pos = strpos($text, 'second partial');
    $partial3_pos = strpos($text, 'third partial');
    $partial4_pos = strpos($text, 'fourth partial');
    $field_pos = strpos($text, 'sample field text');

    $this->assertTrue(($partial4_pos < $field_pos) && ($field_pos < $partial2_pos), 'Text field rendered between two partials.');
  }

  /**
   * Test rendering message text fields other than MESSAGE_FIELD_MESSAGE_TEXT
   * as extra fields.
   */
  function testOtherExtraField() {
    $field = array(
      'field_name' => 'baz',
      'type' => 'text',
      'entity_types' => array('message_type'),
      'settings' => array(
        // Mark this as a message text field.
        'message_text' => TRUE,
      ),
    );
    field_create_field($field);

    $instance = array(
      'field_name' => 'baz',
      'bundle' => 'message_type',
      'entity_type' => 'message_type',
    );
    field_create_instance($instance);

    $message_type = message_type_create('foo');
    $wrapper = entity_metadata_wrapper('message_type', $message_type);
    $wrapper->{MESSAGE_FIELD_MESSAGE_TEXT}[] = array('value' => 'first field', 'format' => 'plain_text');
    $wrapper->baz->set('other field');
    $wrapper->save();

    $message = message_create('foo');
    $build = $message->buildContent();
    $this->assertTrue($build['message__message_text__0']['#markup'], '<p>first field</p>', 'Default message-text field appears correctly.');
    $this->assertTrue($build['message__baz__0']['#markup'], 'other field', 'Non-default message-text field appears correctly.');
  }
}

/**
 * Test the Message and tokens integration.
 */
class MessageTokens extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Message tokens',
      'description' => 'Test the Message and tokens integration.',
      'group' => 'Message',
    );
  }

  function setUp() {
    parent::setUp('message', 'entity_token');
  }

  /**
   * Test token replacement in a message type.
   */
  function testTokens() {
    $user1 = $this->drupalCreateUser();
    $random_text = $this->randomName();
    $token_message = '[message:user:name] ' . $random_text;
    $replaced_message = $user1->name . ' ' . $random_text;

    $message_type = message_type_create('foo');
    $message_type->{MESSAGE_FIELD_MESSAGE_TEXT} = array(
      LANGUAGE_NONE => array(
        0 => array('value' => $token_message, 'format' => 'plain_text'),
      ),
    );
    $message_type->save();

    $message = message_create('foo', array(), $user1);

    $this->assertEqual($replaced_message . "\n", strip_tags($message->getText()), t('Got correct text after token replacement.'));

    // Test not replacing tokens setting enabled.
    $message->data['token replace'] = FALSE;
    $this->assertEqual($token_message . "\n", strip_tags($message->getText()), t('Got correct text without token replacement.'));
  }

  /**
   * Test token hardcoding in a message type.
   */
  function testTokensHardcode() {
    $user1 = $this->drupalCreateUser();
    $name = $user1->name;
    $random_text = $this->randomName();

    $token_messages = array(
      'some text @{message:user} ' . $random_text,
      'some text !{message:user} ' . $random_text,
      'some text %{message:user} ' . $random_text,
      'some text !{wrong:token} ' . $random_text
    );

    $replaced_messages = array(
      'some text ' . $name . ' ' . $random_text,
      'some text ' . $name . ' ' . $random_text,
      'some text <em class="placeholder">' . $name . '</em> ' . $random_text,
      'some text !{wrong:token} ' . $random_text
    );

    $message_type = message_type_create('foo');
    foreach ($token_messages as $token_message) {
      $message_type->{MESSAGE_FIELD_MESSAGE_TEXT}[LANGUAGE_NONE][] = array('value' => $token_message, 'format' => 'plain_text');
    }
    $message_type->save();

    $message = message_create('foo', array(), $user1);

    // Assert the arguments.
    $this->assertTrue(empty($message->arguments), t('No message arguments exist prior to saving the message.'));
    $message->save();
    $this->assertEqual(count($message->arguments), 3, t('Correct number of arguments added after saving the message.'));

    // Assert message is rendered as expected.
    foreach (array_keys($message_type->{MESSAGE_FIELD_MESSAGE_TEXT}[LANGUAGE_NONE]) as $delta) {
      $options = array(
        'partials' => TRUE,
        'partial delta' => $delta,
      );
      // Get text from each partial. We strip the <p> tags, but make sure
      // to keep the <em> tag, so we can assert the token prefixed with
      // '%' sign.
      $this->assertEqual($replaced_messages[$delta] . "\n", strip_tags($message->getText(LANGUAGE_NONE, $options), '<em>'), t('Got correct text for partial @delta after token replacement.', array('@delta' => $delta)));
    }

    // Test no hardcoding.
    $message = message_create('foo', array(), $user1);
    $message->data['skip token hardcode'] = TRUE;
    $message->save();
    $this->assertTrue(empty($message->arguments), t('No message arguments created after saving the message, when "skip token hardcode" is enabled.'));

  }
}



/**
 * Test the Rules integration.
 */
class MessageRulesIntegrationTestCase extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Message Rules integration',
      'description' => 'Tests the message module Rules integration.',
      'group' => 'Message',
      'dependencies' => array('rules'),
    );
  }

  function setUp() {
    parent::setUp('rules', 'message');
  }

  /**
   * Tests creating a message via an action.
   */
  function testCRUD() {
    $message_type = message_type_create('foo', array('message_text' => array(LANGUAGE_NONE => array(array('value' => 'Example text.')))));
    $message_type->save();

    $rule = rule();
    $rule->action('entity_create', array(
      'type' => 'message',
      'param_type' => 'foo',
      'param_user' => entity_metadata_wrapper('user', $GLOBALS['user']),
    ));
    $rule->integrityCheck();
    $rule->execute();

    // Checker whether a new message has been saved.
    $messages = message_load_multiple(FALSE, array('type' => 'foo'));
    $message = reset($messages);

    $this->assertTrue(!empty($message), 'Message has been saved using Rules.');
    $this->assertEqual($message->uid, $GLOBALS['user']->uid , 'Message has been saved for the right user.');
    RulesLog::logger()->checkLog();
  }
}

/**
 * Test the Message cron functionallity.
 */
class MessageCron extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Message purge',
      'description' => 'Test message purging upon cron',
      'group' => 'Message',
    );
  }

  function setUp() {
    parent::setUp('message');
  }

  /**
   * Test purging of messages upon message_cron according to message type purge
   * settings.
   */
  function testPurge() {
    $web_user = $this->drupalCreateUser();

    // Create a purgeable message type with max quota 2 and max days 0.
    $values = array(
      'data' => array(
        'purge' => array(
          'override' => TRUE,
          'enabled' => TRUE,
          'quota' => 2,
          'days' => 0,
        ),
      ),
    );
    $message_type = message_type_create('type1', $values);
    $message_type->save();

    // Make sure the purging data is actually saved.
    $this->assertEqual($message_type->data['purge'], $values['data']['purge'], t('Purge settings are stored in message type.'));

    // Create a purgeable message type with max quota 1 and max days 2.
    $values['data']['purge']['quota'] = 1;
    $values['data']['purge']['days'] = 2;
    $message_type = message_type_create('type2', $values);
    $message_type->save();

    // Create a non purgeable message type with max quota 1 and max days 10.
    $values['data']['purge']['enabled'] = FALSE;
    $values['data']['purge']['quota'] = 1;
    $values['data']['purge']['days'] = 1;
    $message_type = message_type_create('type3', $values);
    $message_type->save();

    $values = array(
      // Set messages creation time to three days ago.
      'timestamp' => time() - 3 * 86400,
    );
    // Create messages.
    for ($i = 0; $i < 4; $i++) {
      $message = message_create('type1', $values, $web_user);
      $message->save();
    }

    for ($i = 0; $i < 3; $i++) {
      $message = message_create('type2', $values, $web_user);
      $message->save();
    }

    for ($i = 0; $i < 3; $i++) {
      $message = message_create('type3', $values, $web_user);
      $message->save();
    }

    // Trigger message's hook_cron().
    message_cron();

    // Four type1 messages were created. The first two should have been
    // deleted.
    $messages = message_load_multiple(FALSE, array('type' => 'type1'));
    $this->assertEqual(array_keys($messages), array(3, 4), t('Two messages deleted due to quota definition.'));

    // All type2 messages should have been deleted.
    $messages = message_load_multiple(FALSE, array('type' => 'type2'));
    $this->assertEqual(array_keys($messages), array(), t('Three messages deleted due to age definition.'));

    // type3 messages should not have been deleted
    $messages = message_load_multiple(FALSE, array('type' => 'type3'));
    $this->assertEqual(array_keys($messages), array(8, 9, 10), t('Messages with disabled purging settings were not deleted.'));
  }

  /**
   * Test compliance with MESSAGE_DELETE_PER_PURGE.
   */
  function testPurgeRequestLimit() {
    // Set maximal amount of messages to delete.
    variable_set('message_delete_cron_limit', 10);

    $web_user = $this->drupalCreateUser();

    // Create a purgeable message type with max quota 2 and max days 0.
    $values = array(
      'data' => array(
        'purge' => array(
          'override' => TRUE,
          'enabled' => TRUE,
          'quota' => 2,
          'days' => 0,
        ),
      ),
    );
    $message_type = message_type_create('type1', $values);
    $message_type->save();
    $message_type = message_type_create('type2', $values);
    $message_type->save();

    // Create more messages than may be deleted in one request.
    for ($i = 0; $i < 10; $i++) {
      $message = message_create('type1', array(), $web_user);
      $message->save();
      $message = message_create('type2', array(), $web_user);
      $message->save();
    }

    // Trigger message's hook_cron().
    message_cron();

    // There are 16 messages to be deleted and 10 deletions allowed, so 8
    // messages of type1 and 2 messages of type2 should be deleted, thus 2
    // messages of type1 and 8 messages of type2 remain.
    $messages = message_load_multiple(FALSE, array('type' => 'type1'));
    $this->assertEqual(count($messages), 2, t('Two messages of type 1 left.'));

    $messages = message_load_multiple(FALSE, array('type' => 'type2'));
    $this->assertEqual(count($messages), 8, t('Eight messages of type 2 left.'));
  }

  /**
   * Test global purge settings and overriding them.
   */
  function testPurgeGlobalSettings() {
    // Set global purge settings.
    variable_set('message_purge_enable', TRUE);
    variable_set('message_purge_quota', 1);
    variable_set('message_purge_days', 2);

    $web_user = $this->drupalCreateUser();

    $message_type = message_type_create('type1');
    $message_type->save();

    // Create an overriding type.
    $values = array(
      'data' => array(
        'purge' => array(
          'override' => TRUE,
          'enabled' => FALSE,
          'quota' => 1,
          'days' => 1,
        ),
      ),
    );
    $message_type = message_type_create('type2', $values);
    $message_type->save();

    $values = array(
      // Set messages creation time to three days ago.
      'timestamp' => time() - 3 * 86400,
    );
    for ($i = 0; $i < 2; $i++) {
      $message = message_create('type1', $values, $web_user);
      $message->save();
      $message = message_create('type2', $values, $web_user);
      $message->save();
    }

    // Trigger message's hook_cron().
    message_cron();

    $messages = message_load_multiple(FALSE, array('type' => 'type1'));
    $this->assertEqual(count($messages), 0, t('All type1 messages deleted.'));

    $messages = message_load_multiple(FALSE, array('type' => 'type2'));
    $this->assertEqual(count($messages), 2, t('Type2 messages were not deleted due to settings override.'));
  }
}

/**
 * Test the Message delete on entity delete functionallity.
 */
class MessageEntityDelete extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Message references',
      'description' => 'Test the Message delete on entity delete functionallity',
      'group' => 'Message',
      'dependencies' => array('entityreference'),
    );
  }

  function setUp() {
    parent::setUp('message', 'entityreference');

    variable_set('message_delete_on_entity_delete', array('node', 'taxonomy_term', 'user'));

    // Create a term reference field.
    $field = array(
      'translatable' => FALSE,
      'entity_types' => array('message'),
      'settings' => array(
        'allowed_values' => array(
          array(
            'vocabulary' => 'terms',
            'parent' => 0,
          ),
        ),
      ),
      'field_name' => 'field_term_ref',
      'type' => 'taxonomy_term_reference',
    );
    $field = field_create_field($field);
    $instance = array(
      'field_name' => 'field_term_ref',
      'bundle' => 'mt1',
      'entity_type' => 'message',
    );
    field_create_instance($instance);

    // Create an multiple-entities-reference field.
    $field = array(
      'translatable' => FALSE,
      'entity_types' => array('message'),
      'settings' => array(
        'handler' => 'base',
        'target_type' => 'node',
        'handler_settings' => array(
          'target_bundles' => array(),
        ),
      ),
      'field_name' => 'field_nodes_ref',
      'type' => 'entityreference',
      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
    );
    $field = field_create_field($field);
    $instance = array(
      'field_name' => 'field_nodes_ref',
      'bundle' => 'mt1',
      'entity_type' => 'message',
    );
    field_create_instance($instance);

    // Create a multiple-terms-reference field.
    $field = array(
      'translatable' => FALSE,
      'entity_types' => array('message'),
      'settings' => array(
        'allowed_values' => array(
          array(
            'vocabulary' => 'terms',
            'parent' => 0,
          ),
        ),
      ),
      'field_name' => 'field_terms_ref',
      'type' => 'taxonomy_term_reference',
      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
    );
    $field = field_create_field($field);
    $instance = array(
      'field_name' => 'field_terms_ref',
      'bundle' => 'mt1',
      'entity_type' => 'message',
    );
    field_create_instance($instance);

    // Create an entity reference field.
    $field = array(
      'translatable' => FALSE,
      'entity_types' => array('message'),
      'settings' => array(
        'handler' => 'base',
        'target_type' => 'node',
        'handler_settings' => array(
          'target_bundles' => array(),
        ),
      ),
      'field_name' => 'field_node_ref',
      'type' => 'entityreference',
    );
    $field = field_create_field($field);
    $instance = array(
      'field_name' => 'field_node_ref',
      'bundle' => 'mt1',
      'entity_type' => 'message',
    );
    field_create_instance($instance);

    // Create an user entity reference field.
    $field = array(
      'translatable' => FALSE,
      'entity_types' => array('message'),
      'settings' => array(
        'handler' => 'base',
        'target_type' => 'user',
        'handler_settings' => array(
          'target_bundles' => array(),
        ),
      ),
      'field_name' => 'field_user_ref',
      'type' => 'entityreference',
    );
    $field = field_create_field($field);
    $instance = array(
      'field_name' => 'field_user_ref',
      'bundle' => 'mt1',
      'entity_type' => 'message',
    );
    field_create_instance($instance);

    // Create a terms vocobulary.
    $vocabulary = new stdClass();
    $vocabulary->name = 'Terms';
    $vocabulary->machine_name = 'terms';
    taxonomy_vocabulary_save($vocabulary);

    $message_type = message_type_create('mt1', array());
    $message_type->save();

    // Create nodes and terms.
    for ($i = 1; $i <= 5; $i++) {
      $node = new stdClass();
      $node->type = 'article';
      node_object_prepare($node);
      $node->title = "node $i";
      $node->language = LANGUAGE_NONE;
      node_save($node);

      $term = new stdClass();
      $term->name = "term $i";
      $term->vid = 1;
      taxonomy_term_save($term);
    }
  }

  /**
   * Test deletion of a message after its referenced entities have been deleted.
   */
  function testReferencedEntitiesDelete() {
    // Test entities reference.
    $message = message_create('mt1', array());
    $wrapper = entity_metadata_wrapper('message', $message);
    $wrapper->field_nodes_ref->set(array(1, 2));
    $wrapper->save();
    node_delete(2);
    $message = message_load($message->mid);
    $this->assertTrue($message, 'Message exists after deleting one of two referenced nodes.');
    node_delete(1);
    $message = message_load($message->mid);
    $this->assertTrue(empty($message), 'Message deleted after deleting all referenced nodes.');

    // Test terms reference.
    $message = message_create('mt1', array());
    $wrapper = entity_metadata_wrapper('message', $message);
    $wrapper->field_terms_ref->set(array(1, 2));
    $wrapper->save();
    taxonomy_term_delete(2);
    $message = message_load($message->mid);
    $this->assertTrue($message, 'Message exists after deleting one of two referenced terms.');
    taxonomy_term_delete(1);
    $message = message_load($message->mid);
    $this->assertTrue(empty($message), 'Message deleted after deleting all referenced terms.');

    $message = message_create('mt1', array());
    $wrapper = entity_metadata_wrapper('message', $message);
    $wrapper->field_terms_ref->set(array(3));
    $wrapper->save();
    taxonomy_term_delete(3);
    $message = message_load($message->mid);
    $this->assertTrue(empty($message), 'Message deleted after deleting single referenced term.');

    $message = message_create('mt1', array());
    $wrapper = entity_metadata_wrapper('message', $message);
    $wrapper->field_terms_ref->set(array(4, 5));
    $wrapper->field_term_ref->set(4);
    $wrapper->save();
    taxonomy_term_delete(4);
    $message = message_load($message->mid);
    $this->assertTrue(empty($message), 'Message deleted after deleting single referenced term while another the message still references other term in another field.');

    // Test user reference.
    $account = $this->drupalCreateUser();
    $message = message_create('mt1', array());
    $wrapper = entity_metadata_wrapper('message', $message);
    $wrapper->field_user_ref->set($account->uid);
    $wrapper->save();
    user_delete($account->uid);
    $message = message_load($message->mid);
    $this->assertTrue(empty($message), 'Message deleted after deleting single referenced user.');
  }
}
