<?php
// $Id: text.test,v 1.10 2009/08/22 00:58:53 webchick Exp $

class ComboFieldTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name'  => 'Combo Field',
      'description'  => "Test Combo Fields.",
      'group' => 'Field'
    );
  }

  // TODO: move this elsewhere
  /**
   * Recursively compare two arrays.
   *
   * @param $op1
   *   An array.
   * @param $op2
   *   An array
   * @returns
   *   -1, 0, or 1 based on whether $op1 is less than, equal to, or
   *   greater than $op2, or return NULL if the two arrays are not
   *   comparable. An array is less than another array if it has fewer
   *   keys, or if one of its values is less than the corresponding
   *   value in the other.  Arrays are not comparable if a key exists
   *   in one but not the other, or if a key is an array in one but
   *   not the other.  If this function returns -1 or 1, it does not
   *   guarantee that the arrays are fully comparable, only that it
   *   encountered a comparable difference before encountering an
   *   uncomparable difference.  If the function returns 0, the arrays
   *   are guaranteed to be both equal and comparable.
   */
  static function recursive_array_compare($op1, $op2)
  {
    if (count($op1) < count($op2)) {
      return -1; // $op1 < $op2
    } elseif (count($op1) > count($op2)) {
      return 1; // $op1 > $op2
    }
    foreach ($op1 as $key => $val) {
      if (!array_key_exists($key, $op2)) {
        return null; // uncomparable
      } else if (is_array($val)) {
        if (is_array($op2[$key])) {
          $ret = ComboFieldTestCase::recursive_array_compare($val, $op2[$key]);
          if ($ret !== 0) {
            return $ret;
          }
        }
        else {
          // If $op1[$key] is an array and $op2[$key] is not, uncomparable.
          return null;
        }
      } elseif (is_array($op2[$key])) {
        // If $op1[$key] is not an array and $op2[$key] is, uncomparable.
        return null;
      } elseif ($val < $op2[$key]) {
        return -1;
      } elseif ($val > $op2[$key]) {
        return 1;
      }
    }
    return 0; // $op1 == $op2
  }

  function setUp() {
    parent::setUp('combo', 'field_test');
  }

  function testComboBasics() {
    // Create a combo field c1, and verify combo entity bundle info.
    $type = entity_get_info('combo');
    $this->assertTrue(!empty($type), 'Entity type combo exists.');
    $this->assertTrue(empty($type['bundles']), 'No combo bundles exist.');
    $combo_field = array('type' => 'combo', 'field_name' => 'c1', 'cardinality' => 2);
    field_create_field($combo_field);
    $type = entity_get_info('combo');
    $this->assertEqual(count($type['bundles']), 1, 'One combo bundle exists.');
    $this->assertTrue(!empty($type['bundles']['c1']), 'Combo bundle c1 exists.');

    // Create field instances on the combo entity c1 with
    // cardinalities 1, 3, and unlimited, and verify.
    $instances = field_info_instances('c1');
    $this->assertEqual(count($instances), 0, 'No instances on bundle c1 exist.');
    $field = array('type' => 'text', 'field_name' => 'card_1');
    field_create_field($field);
    $instance = array('field_name' => 'card_1', 'bundle' => 'c1');
    field_create_instance($instance);
    $field = array('type' => 'text', 'field_name' => 'card_3', 'cardinality' => 3);
    field_create_field($field);
    $instance = array('field_name' => 'card_3', 'bundle' => 'c1');
    field_create_instance($instance);
    $field = array('type' => 'text', 'field_name' => 'card_unlim', 'cardinality' => FIELD_CARDINALITY_UNLIMITED);
    field_create_field($field);
    $instance = array('field_name' => 'card_unlim', 'bundle' => 'c1');
    field_create_instance($instance);
    $instances = field_info_instances('c1');
    $this->assertEqual(count($instances), 3, 'Three instance on combo bundle exists.');
    $this->assertEqual($instances['card_1']['bundle'], 'c1', 'Instance for card_1 on c1 exits.');
    $this->assertEqual($instances['card_3']['bundle'], 'c1', 'Instance for card_3 on c1 exits.');
    $this->assertEqual($instances['card_unlim']['bundle'], 'c1', 'Instance for card_3 on c1 exits.');

    // Create a field instance of c1 on a test entity bundle.
    $instance = array('field_name' => 'c1', 'bundle' => FIELD_TEST_BUNDLE);
    field_create_instance($instance);
    $instances = field_info_instances(FIELD_TEST_BUNDLE);
    $this->assertEqual(count($instances), 1, 'Once instance on test_bundle exists.');
    $this->assertEqual($instances['c1']['bundle'], FIELD_TEST_BUNDLE, 'Instance for c1 on test_bundle.');

    // Save a test entity with combo field data.
    $insert_entity = field_test_create_stub_entity();
    $insert_entity->c1 = array(
      'zxx' => array(
        0 => array(
          'card_1' => array(
            'zxx' => array(
              0 => array(
                'value' => 'card_1 delta 0',
                'format' => 0,
              ),
            ),
          ),
          'card_3' => array(
            'zxx' => array(
              0 => array(
                'value' => 'card_3 delta 0',
                'format' => 0,
              ),
              1 => array(
                'value' => 'card_3 delta 1',
                'format' => 0,
              ),
              2 => array(
                'value' => 'card_3 delta 2',
                'format' => 0,
              ),
            ),
          ),
          'card_unlim' => array(
            'zxx' => array(
              0 => array(
                'value' => 'card_unlim delta 0',
                'format' => 0,
              ),
              1 => array(
                'value' => 'card_unlim delta 1',
                'format' => 0,
              ),
              2 => array(
                'value' => 'card_unlim delta 2',
                'format' => 0,
              ),
              3 => array(
                'value' => 'card_unlim delta 3',
                'format' => 0,
              ),
              4 => array(
                'value' => 'card_unlim delta 4',
                'format' => 0,
              ),
            ),
          ),
        ),
      ),
    );
    $insert_entity->c1['zxx'][1] = $insert_entity->c1['zxx'][0];

    field_attach_insert('test_entity', $insert_entity);
    $this->assertTrue(isset($insert_entity->c1['zxx'][0]['deltas']), 'Combo field deltas is set');

    // Verifying loading the combo entity created by saving the test_entity.
    $combo_entity = _combo_create_entity('test_entity', $insert_entity, $combo_field, NULL /* TODO NOT USED */);
    field_attach_load('combo', array($combo_entity->id => $combo_entity));
    // TODO: recursive_array_compare is really not the comparison I
    // want, so I have to unset the 'safe' item.
    //print_r($combo_entity);
    unset($combo_entity->card_1['zxx'][0]['safe']);
    unset($combo_entity->card_3['zxx'][0]['safe']);
    unset($combo_entity->card_3['zxx'][1]['safe']);
    unset($combo_entity->card_3['zxx'][2]['safe']);
    unset($combo_entity->card_unlim['zxx'][0]['safe']);
    unset($combo_entity->card_unlim['zxx'][1]['safe']);
    unset($combo_entity->card_unlim['zxx'][2]['safe']);
    unset($combo_entity->card_unlim['zxx'][3]['safe']);
    unset($combo_entity->card_unlim['zxx'][4]['safe']);
    $this->assertTrue($this->recursive_array_compare($combo_entity->card_1, $insert_entity->c1['zxx'][0]['card_1']) === 0, 'Combo entity field card_1 loads correctly.');
    $this->assertTrue($this->recursive_array_compare($combo_entity->card_3, $insert_entity->c1['zxx'][0]['card_3']) === 0, 'Combo entity field card_3 loads correctly.');
    $this->assertTrue($this->recursive_array_compare($combo_entity->card_unlim, $insert_entity->c1['zxx'][0]['card_unlim']) === 0, 'Combo entity field card_unlim loads correctly.');
    
    // Verify loading the test entity.
    $load_entity = field_test_create_stub_entity();
    field_attach_load('test_entity', array($load_entity->ftid => $load_entity));
    // TODO: recursive_array_compare is really not the comparison I
    // want, so I have to unset the 'safe' item.
    foreach (array_keys($insert_entity->c1['zxx']) as $combo_delta) {
      unset($load_entity->c1['zxx'][$combo_delta]['card_1']['zxx'][0]['safe']);
      unset($load_entity->c1['zxx'][$combo_delta]['card_3']['zxx'][0]['safe']);
      unset($load_entity->c1['zxx'][$combo_delta]['card_3']['zxx'][1]['safe']);
      unset($load_entity->c1['zxx'][$combo_delta]['card_3']['zxx'][2]['safe']);
      unset($load_entity->c1['zxx'][$combo_delta]['card_unlim']['zxx'][0]['safe']);
      unset($load_entity->c1['zxx'][$combo_delta]['card_unlim']['zxx'][1]['safe']);
      unset($load_entity->c1['zxx'][$combo_delta]['card_unlim']['zxx'][2]['safe']);
      unset($load_entity->c1['zxx'][$combo_delta]['card_unlim']['zxx'][3]['safe']);
      unset($load_entity->c1['zxx'][$combo_delta]['card_unlim']['zxx'][4]['safe']);
      $this->assertTrue($this->recursive_array_compare($load_entity->c1['zxx'][$combo_delta]['card_1'], $insert_entity->c1['zxx'][$combo_delta]['card_1']) === 0, "Test entity field c1[delta $combo_delta]->card_1 loads correctly.");
      $this->assertTrue($this->recursive_array_compare($load_entity->c1['zxx'][$combo_delta]['card_3'], $insert_entity->c1['zxx'][$combo_delta]['card_3']) === 0, "Test entity field c1[delta $combo_delta]->card_3 loads correctly.");
      $this->assertTrue($this->recursive_array_compare($load_entity->c1['zxx'][$combo_delta]['card_unlim'], $insert_entity->c1['zxx'][$combo_delta]['card_unlim']) === 0, "Test entity field c1[delta $combo_delta]->card_unlim loads correctly.");
    }
      
    print_r($load_entity);
  }
}
