<?php
/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

require_once('test_helper.php');

class DatumIOTest extends PHPUnit_Framework_TestCase
{
  /**
   * @dataProvider data_provider
   */
  function test_datum_round_trip($schema_json, $datum, $binary)
  {
    $schema = AvroSchema::parse($schema_json);
    $written = new AvroStringIO();
    $encoder = new AvroIOBinaryEncoder($written);
    $writer = new AvroIODatumWriter($schema);

    $writer->write($datum, $encoder);
    $output = strval($written);
    $this->assertEquals($binary, $output,
                        sprintf("expected: %s\n  actual: %s",
                                AvroDebug::ascii_string($binary, 'hex'),
                                AvroDebug::ascii_string($output, 'hex')));

    $read = new AvroStringIO($binary);
    $decoder = new AvroIOBinaryDecoder($read);
    $reader = new AvroIODatumReader($schema);
    $read_datum = $reader->read($decoder);
    $this->assertEquals($datum, $read_datum);
  }

  function data_provider()
  {
    return array(array('"null"', null, ''),

                 array('"boolean"', true, "\001"),
                 array('"boolean"', false, "\000"),

                 array('"int"', (int) -2147483648, "\xFF\xFF\xFF\xFF\x0F"),
                 array('"int"', -1, "\001"),
                 array('"int"', 0, "\000"),
                 array('"int"', 1, "\002"),
                 array('"int"', 2147483647, "\xFE\xFF\xFF\xFF\x0F"),

                 // array('"long"', (int) -9223372036854775808, "\001"),
                 array('"long"', -1, "\001"),
                 array('"long"',  0, "\000"),
                 array('"long"',  1, "\002"),
                 // array('"long"', 9223372036854775807, "\002")

                 array('"float"', (float) -10.0, "\000\000 \301"),
                 array('"float"', (float)  -1.0, "\000\000\200\277"),
                 array('"float"', (float)   0.0, "\000\000\000\000"),
                 array('"float"', (float)   2.0, "\000\000\000@"),
                 array('"float"', (float)   9.0, "\000\000\020A"),

                 array('"double"', (double) -10.0, "\000\000\000\000\000\000$\300"),
                 array('"double"', (double) -1.0, "\000\000\000\000\000\000\360\277"),
                 array('"double"', (double) 0.0, "\000\000\000\000\000\000\000\000"),
                 array('"double"', (double) 2.0, "\000\000\000\000\000\000\000@"),
                 array('"double"', (double) 9.0, "\000\000\000\000\000\000\"@"),

                 array('"string"', 'foo', "\x06foo"),
                 array('"bytes"', "\x01\x02\x03", "\x06\x01\x02\x03"),

                 array('{"type":"array","items":"int"}',
                       array(1,2,3),
                       "\x06\x02\x04\x06\x00"),
                 array('{"type":"map","values":"int"}',
                       array('foo' => 1, 'bar' => 2, 'baz' => 3),
                       "\x06\x06foo\x02\x06bar\x04\x06baz\x06\x00"),
                 array('["null", "int"]', 1, "\x02\x02"),
                 array('{"name":"fix","type":"fixed","size":3}',
                       "\xAA\xBB\xCC", "\xAA\xBB\xCC"),
                 array('{"name":"enm","type":"enum","symbols":["A","B","C"]}',
                       'B', "\x02"),
                 array('{"name":"rec","type":"record","fields":[{"name":"a","type":"int"},{"name":"b","type":"boolean"}]}',
                       array('a' => 1, 'b' => false),
                       "\x02\x00")
      );
  }

  function default_provider()
  {
    return array(array('"null"', 'null', null),
                 array('"boolean"', 'true', true),
                 array('"int"', '1', 1),
                 array('"long"', '2000', 2000),
                 array('"float"', '1.1', (float) 1.1),
                 array('"double"', '200.2', (double) 200.2),
                 array('"string"', '"quux"', 'quux'),
                 array('"bytes"', '"\u00FF"', "\xC3\xBF"),
                 array('{"type":"array","items":"int"}',
                       '[5,4,3,2]', array(5,4,3,2)),
                 array('{"type":"map","values":"int"}',
                       '{"a":9}', array('a' => 9)),
                 array('["int","string"]', '8', 8),
                 array('{"name":"x","type":"enum","symbols":["A","V"]}',
                       '"A"', 'A'),
                 array('{"name":"x","type":"fixed","size":4}', '"\u00ff"', "\xC3\xBF"),
                 array('{"name":"x","type":"record","fields":[{"name":"label","type":"int"}]}',
                       '{"label":7}', array('label' => 7)));
  }

  /**
   * @dataProvider default_provider
   */
  function test_field_default_value($field_schema_json,
                                    $default_json, $default_value)
  {
    $writers_schema_json = '{"name":"foo","type":"record","fields":[]}';
    $writers_schema = AvroSchema::parse($writers_schema_json);

    $readers_schema_json = sprintf(
      '{"name":"foo","type":"record","fields":[{"name":"f","type":%s,"default":%s}]}',
      $field_schema_json, $default_json);
    $readers_schema = AvroSchema::parse($readers_schema_json);

    $reader = new AvroIODatumReader($writers_schema, $readers_schema);
    $record = $reader->read(new AvroIOBinaryDecoder(new AvroStringIO()));
    if (array_key_exists('f', $record))
      $this->assertEquals($default_value, $record['f']);
    else
      $this->assertTrue(false, sprintf('expected field record[f]: %s',
                                       print_r($record, true))) ;
  }

}
