avro 1.8.2 → 1.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env ruby
2
1
  # Licensed to the Apache Software Foundation (ASF) under one
3
2
  # or more contributor license agreements. See the NOTICE file
4
3
  # distributed with this work for additional information
@@ -7,7 +6,7 @@
7
6
  # "License"); you may not use this file except in compliance
8
7
  # with the License. You may obtain a copy of the License at
9
8
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # https://www.apache.org/licenses/LICENSE-2.0
11
10
  #
12
11
  # Unless required by applicable law or agreed to in writing, software
13
12
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -27,15 +26,17 @@ class RandomData
27
26
  end
28
27
 
29
28
  def nextdata(schm, d=0)
29
+ return logical_nextdata(schm, d=0) unless schm.type_adapter.eql?(Avro::LogicalTypes::Identity)
30
+
30
31
  case schm.type_sym
31
32
  when :boolean
32
33
  rand > 0.5
33
34
  when :string
34
35
  randstr()
35
36
  when :int
36
- rand(Avro::Schema::INT_MAX_VALUE - Avro::Schema::INT_MIN_VALUE) + Avro::Schema::INT_MIN_VALUE
37
+ rand_int
37
38
  when :long
38
- rand(Avro::Schema::LONG_MAX_VALUE - Avro::Schema::LONG_MIN_VALUE) + Avro::Schema::LONG_MIN_VALUE
39
+ rand_long
39
40
  when :float
40
41
  (-1024 + 2048 * rand).round.to_f
41
42
  when :double
@@ -79,6 +80,17 @@ class RandomData
79
80
  end
80
81
  end
81
82
 
83
+ def logical_nextdata(schm, _d=0)
84
+ case schm.logical_type
85
+ when 'date'
86
+ Avro::LogicalTypes::IntDate.decode(rand_int)
87
+ when 'timestamp-micros'
88
+ Avro::LogicalTypes::TimestampMicros.decode(rand_long)
89
+ when 'timestamp-millis'
90
+ Avro::LogicalTypes::TimestampMillis.decode(rand_long)
91
+ end
92
+ end
93
+
82
94
  CHARPOOL = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789'
83
95
  BYTEPOOL = '12345abcd'
84
96
 
@@ -87,4 +99,12 @@ class RandomData
87
99
  rand(length+1).times { str << chars[rand(chars.size)] }
88
100
  str
89
101
  end
102
+
103
+ def rand_int
104
+ rand(Avro::Schema::INT_MAX_VALUE - Avro::Schema::INT_MIN_VALUE) + Avro::Schema::INT_MIN_VALUE
105
+ end
106
+
107
+ def rand_long
108
+ rand(Avro::Schema::LONG_MAX_VALUE - Avro::Schema::LONG_MIN_VALUE) + Avro::Schema::LONG_MIN_VALUE
109
+ end
90
110
  end
@@ -7,7 +7,7 @@
7
7
  # "License"); you may not use this file except in compliance
8
8
  # with the License. You may obtain a copy of the License at
9
9
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
10
+ # https://www.apache.org/licenses/LICENSE-2.0
11
11
  #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -7,7 +7,7 @@
7
7
  # "License"); you may not use this file except in compliance
8
8
  # with the License. You may obtain a copy of the License at
9
9
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
10
+ # https://www.apache.org/licenses/LICENSE-2.0
11
11
  #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -6,7 +6,7 @@
6
6
  # "License"); you may not use this file except in compliance
7
7
  # with the License. You may obtain a copy of the License at
8
8
  #
9
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
10
  #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -7,7 +7,7 @@
7
7
  # "License"); you may not use this file except in compliance
8
8
  # with the License. You may obtain a copy of the License at
9
9
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
10
+ # https://www.apache.org/licenses/LICENSE-2.0
11
11
  #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -7,7 +7,7 @@
7
7
  # "License"); you may not use this file except in compliance
8
8
  # with the License. You may obtain a copy of the License at
9
9
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
10
+ # https://www.apache.org/licenses/LICENSE-2.0
11
11
  #
12
12
  # Unless required by applicable law or agreed to in writing, software
13
13
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,13 +20,13 @@ require 'test_help'
20
20
  class TestDataFile < Test::Unit::TestCase
21
21
  HERE = File.expand_path File.dirname(__FILE__)
22
22
  def setup
23
- if File.exists?(HERE + '/data.avr')
23
+ if File.exist?(HERE + '/data.avr')
24
24
  File.unlink(HERE + '/data.avr')
25
25
  end
26
26
  end
27
27
 
28
28
  def teardown
29
- if File.exists?(HERE + '/data.avr')
29
+ if File.exist?(HERE + '/data.avr')
30
30
  File.unlink(HERE + '/data.avr')
31
31
  end
32
32
  end
@@ -38,7 +38,7 @@ class TestDataFile < Test::Unit::TestCase
38
38
  "fields" : [
39
39
  {"name": "username", "type": "string"},
40
40
  {"name": "age", "type": "int"},
41
- {"name": "verified", "type": "boolean", "default": "false"}
41
+ {"name": "verified", "type": "boolean", "default": false}
42
42
  ]}
43
43
  JSON
44
44
 
@@ -180,6 +180,19 @@ JSON
180
180
  assert_equal records, ['a' * 10_000]
181
181
  end
182
182
 
183
+ def test_zstandard
184
+ Avro::DataFile.open('data.avr', 'w', '"string"', :zstandard) do |writer|
185
+ writer << 'a' * 10_000
186
+ end
187
+ assert(File.size('data.avr') < 600)
188
+
189
+ records = []
190
+ Avro::DataFile.open('data.avr') do |reader|
191
+ reader.each {|record| records << record }
192
+ end
193
+ assert_equal records, ['a' * 10_000]
194
+ end
195
+
183
196
  def test_append_to_deflated_file
184
197
  schema = Avro::Schema.parse('"string"')
185
198
  writer = Avro::IO::DatumWriter.new(schema)
@@ -6,7 +6,7 @@
6
6
  # "License"); you may not use this file except in compliance
7
7
  # with the License. You may obtain a copy of the License at
8
8
  #
9
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
10
  #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -34,4 +34,23 @@ class TestFingerprints < Test::Unit::TestCase
34
34
  assert_equal 28572620203319713300323544804233350633246234624932075150020181448463213378117,
35
35
  schema.sha256_fingerprint
36
36
  end
37
+
38
+ def test_crc_64_avro_fingerprint
39
+ schema = Avro::Schema.parse <<-SCHEMA
40
+ { "type": "int" }
41
+ SCHEMA
42
+
43
+ assert_equal 8247732601305521295, # hex: 0x7275d51a3f395c8f
44
+ schema.crc_64_avro_fingerprint
45
+ end
46
+
47
+ # This definitely belongs somewhere else
48
+ def test_single_object_encoding_header
49
+ schema = Avro::Schema.parse <<-SCHEMA
50
+ { "type": "int" }
51
+ SCHEMA
52
+
53
+ assert_equal ["c3", "01", "8f", "5c", "39", "3f", "1a", "D5", "75", "72"].map{|e| e.to_i(16) },
54
+ schema.single_object_encoding_header
55
+ end
37
56
  end
@@ -6,7 +6,7 @@
6
6
  # "License"); you may not use this file except in compliance
7
7
  # with the License. You may obtain a copy of the License at
8
8
  #
9
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
10
  #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -6,7 +6,7 @@
6
6
  # "License"); you may not use this file except in compliance
7
7
  # with the License. You may obtain a copy of the License at
8
8
  #
9
- # http://www.apache.org/licenses/LICENSE-2.0
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
10
  #
11
11
  # Unless required by applicable law or agreed to in writing, software
12
12
  # distributed under the License is distributed on an "AS IS" BASIS,
@@ -84,6 +84,20 @@ EOS
84
84
  check_default(record_schema, '{"f": 11}', {"f" => 11})
85
85
  end
86
86
 
87
+ def test_record_with_logical_type
88
+ record_schema = <<EOS
89
+ {"type": "record",
90
+ "name": "Test",
91
+ "fields": [{"name": "ts",
92
+ "type": {"type": "long",
93
+ "logicalType": "timestamp-micros"}},
94
+ {"name": "ts2",
95
+ "type": {"type": "long",
96
+ "logicalType": "timestamp-millis"}}]}
97
+ EOS
98
+ check(record_schema)
99
+ end
100
+
87
101
  def test_error
88
102
  error_schema = <<EOS
89
103
  {"type": "error",
@@ -101,6 +115,13 @@ EOS
101
115
  check_default(enum_schema, '"B"', "B")
102
116
  end
103
117
 
118
+ def test_enum_with_default
119
+ enum_schema = '{"type": "enum", "name": "Test", "symbols": ["A", "B"], "default": "A"}'
120
+ check(enum_schema)
121
+ # Field default is used for missing field.
122
+ check_default(enum_schema, '"B"', "B")
123
+ end
124
+
104
125
  def test_recursive
105
126
  recursive_schema = <<EOS
106
127
  {"type": "record",
@@ -115,6 +136,7 @@ EOS
115
136
  def test_union
116
137
  union_schema = <<EOS
117
138
  ["string",
139
+ {"type": "int", "logicalType": "date"},
118
140
  "null",
119
141
  "long",
120
142
  {"type": "record",
@@ -146,10 +168,42 @@ EOS
146
168
  check_default(fixed_schema, '"a"', "a")
147
169
  end
148
170
 
171
+ def test_record_variable_key_types
172
+ datum = { sym: "foo", "str"=>"bar"}
173
+ ret_val = { "sym"=> "foo", "str"=>"bar"}
174
+ schema = Schema.parse('{"type":"record", "name":"rec", "fields":[{"name":"sym", "type":"string"}, {"name":"str", "type":"string"}]}')
175
+
176
+ writer, _encoder, _datum_writer = write_datum(datum, schema)
177
+
178
+ ret_datum = read_datum(writer, schema)
179
+ assert_equal ret_datum, ret_val
180
+ end
181
+
182
+ def test_record_with_nil
183
+ schema = Avro::Schema.parse('{"type":"record", "name":"rec", "fields":[{"type":"int", "name":"i"}]}')
184
+ assert_raise(Avro::IO::AvroTypeError) do
185
+ write_datum(nil, schema)
186
+ end
187
+ end
188
+
189
+ def test_array_with_nil
190
+ schema = Avro::Schema.parse('{"type":"array", "items":"int"}')
191
+ assert_raise(Avro::IO::AvroTypeError) do
192
+ write_datum(nil, schema)
193
+ end
194
+ end
195
+
196
+ def test_map_with_nil
197
+ schema = Avro::Schema.parse('{"type":"map", "values":"long"}')
198
+ assert_raise(Avro::IO::AvroTypeError) do
199
+ write_datum(nil, schema)
200
+ end
201
+ end
202
+
149
203
  def test_enum_with_duplicate
150
204
  str = '{"type": "enum", "name": "Test","symbols" : ["AA", "AA"]}'
151
- assert_raises(Avro::SchemaParseError) do
152
- schema = Avro::Schema.parse str
205
+ assert_raises(Avro::SchemaParseError.new('Duplicate symbol: ["AA", "AA"]')) do
206
+ Avro::Schema.parse str
153
207
  end
154
208
  end
155
209
 
@@ -256,7 +310,7 @@ EOS
256
310
  end
257
311
 
258
312
  def test_skip_long
259
- for value_to_skip, hex_encoding in BINARY_INT_ENCODINGS
313
+ for value_to_skip, _hex_encoding in BINARY_INT_ENCODINGS
260
314
  value_to_read = 6253
261
315
 
262
316
  # write some data in binary to string buffer
@@ -281,7 +335,7 @@ EOS
281
335
  end
282
336
 
283
337
  def test_skip_int
284
- for value_to_skip, hex_encoding in BINARY_INT_ENCODINGS
338
+ for value_to_skip, _hex_encoding in BINARY_INT_ENCODINGS
285
339
  value_to_read = 6253
286
340
 
287
341
  writer = StringIO.new
@@ -331,7 +385,7 @@ EOS
331
385
  datum_to_write = 219
332
386
  for rs in promotable_schemas[(i + 1)..-1]
333
387
  readers_schema = Avro::Schema.parse(rs)
334
- writer, enc, dw = write_datum(datum_to_write, writers_schema)
388
+ writer, _enc, _dw = write_datum(datum_to_write, writers_schema)
335
389
  datum_read = read_datum(writer, writers_schema, readers_schema)
336
390
  if datum_read != datum_to_write
337
391
  incorrect += 1
@@ -341,6 +395,100 @@ EOS
341
395
  end
342
396
  end
343
397
 
398
+ def test_interchangeable_schemas
399
+ interchangeable_schemas = ['"string"', '"bytes"']
400
+ incorrect = 0
401
+ interchangeable_schemas.each_with_index do |ws, i|
402
+ writers_schema = Avro::Schema.parse(ws)
403
+ datum_to_write = 'foo'
404
+ readers_schema = Avro::Schema.parse(interchangeable_schemas[i == 0 ? 1 : 0])
405
+ writer, * = write_datum(datum_to_write, writers_schema)
406
+ datum_read = read_datum(writer, writers_schema, readers_schema)
407
+ if datum_read != datum_to_write
408
+ incorrect += 1
409
+ end
410
+ end
411
+ assert_equal(incorrect, 0)
412
+ end
413
+
414
+ def test_unknown_enum_symbol
415
+ writers_schema = Avro::Schema.parse(<<-SCHEMA)
416
+ {
417
+ "type": "enum",
418
+ "name": "test",
419
+ "symbols": ["B", "C"]
420
+ }
421
+ SCHEMA
422
+ readers_schema = Avro::Schema.parse(<<-SCHEMA)
423
+ {
424
+ "type": "enum",
425
+ "name": "test",
426
+ "symbols": ["A", "B"]
427
+ }
428
+ SCHEMA
429
+ datum_to_write = "C"
430
+ writer, * = write_datum(datum_to_write, writers_schema)
431
+ datum_read = read_datum(writer, writers_schema, readers_schema)
432
+ # Ruby implementation did not follow the spec and returns the writer's symbol here
433
+ assert_equal(datum_read, datum_to_write)
434
+ end
435
+
436
+ def test_unknown_enum_symbol_with_enum_default
437
+ writers_schema = Avro::Schema.parse(<<-SCHEMA)
438
+ {
439
+ "type": "enum",
440
+ "name": "test",
441
+ "symbols": ["B", "C"]
442
+ }
443
+ SCHEMA
444
+ readers_schema = Avro::Schema.parse(<<-SCHEMA)
445
+ {
446
+ "type": "enum",
447
+ "name": "test",
448
+ "symbols": ["A", "B", "UNKNOWN"],
449
+ "default": "UNKNOWN"
450
+ }
451
+ SCHEMA
452
+ datum_to_write = "C"
453
+ writer, * = write_datum(datum_to_write, writers_schema)
454
+ datum_read = read_datum(writer, writers_schema, readers_schema)
455
+ assert_equal(datum_read, "UNKNOWN")
456
+ end
457
+
458
+ def test_array_schema_promotion
459
+ writers_schema = Avro::Schema.parse('{"type":"array", "items":"int"}')
460
+ readers_schema = Avro::Schema.parse('{"type":"array", "items":"long"}')
461
+ datum_to_write = [1, 2]
462
+ writer, * = write_datum(datum_to_write, writers_schema)
463
+ datum_read = read_datum(writer, writers_schema, readers_schema)
464
+ assert_equal(datum_read, datum_to_write)
465
+ end
466
+
467
+ def test_map_schema_promotion
468
+ writers_schema = Avro::Schema.parse('{"type":"map", "values":"int"}')
469
+ readers_schema = Avro::Schema.parse('{"type":"map", "values":"long"}')
470
+ datum_to_write = { 'foo' => 1, 'bar' => 2 }
471
+ writer, * = write_datum(datum_to_write, writers_schema)
472
+ datum_read = read_datum(writer, writers_schema, readers_schema)
473
+ assert_equal(datum_read, datum_to_write)
474
+ end
475
+
476
+ def test_aliased
477
+ writers_schema = Avro::Schema.parse(<<-SCHEMA)
478
+ {"type":"record", "name":"Rec1", "fields":[
479
+ {"name":"field1", "type":"int"}
480
+ ]}
481
+ SCHEMA
482
+ readers_schema = Avro::Schema.parse(<<-SCHEMA)
483
+ {"type":"record", "name":"Rec2", "aliases":["Rec1"], "fields":[
484
+ {"name":"field2", "aliases":["field1"], "type":"int"}
485
+ ]}
486
+ SCHEMA
487
+ writer, * = write_datum({ 'field1' => 1 }, writers_schema)
488
+ datum_read = read_datum(writer, writers_schema, readers_schema)
489
+ assert_equal(datum_read, { 'field2' => 1 })
490
+ end
491
+
344
492
  def test_snappy_backward_compat
345
493
  # a snappy-compressed block payload without the checksum
346
494
  # this has no back-references, just one literal so the last 9
@@ -417,7 +565,7 @@ EOS
417
565
 
418
566
  def checkser(schm, randomdata)
419
567
  datum = randomdata.next
420
- assert validate(schm, datum)
568
+ assert validate(schm, datum), 'datum is not valid for schema'
421
569
  w = Avro::IO::DatumWriter.new(schm)
422
570
  writer = StringIO.new "", "w"
423
571
  w.write(datum, Avro::IO::BinaryEncoder.new(writer))
@@ -0,0 +1,128 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Licensed to the Apache Software Foundation (ASF) under one
3
+ # or more contributor license agreements. See the NOTICE file
4
+ # distributed with this work for additional information
5
+ # regarding copyright ownership. The ASF licenses this file
6
+ # to you under the Apache License, Version 2.0 (the
7
+ # "License"); you may not use this file except in compliance
8
+ # with the License. You may obtain a copy of the License at
9
+ #
10
+ # https://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require 'test_help'
19
+
20
+ class TestLogicalTypes < Test::Unit::TestCase
21
+ def test_int_date
22
+ schema = Avro::Schema.parse <<-SCHEMA
23
+ { "type": "int", "logicalType": "date" }
24
+ SCHEMA
25
+
26
+ assert_equal 'date', schema.logical_type
27
+ today = Date.today
28
+ assert_encode_and_decode today, schema
29
+ assert_preencoded Avro::LogicalTypes::IntDate.encode(today), schema, today
30
+ end
31
+
32
+ def test_int_date_conversion
33
+ type = Avro::LogicalTypes::IntDate
34
+
35
+ assert_equal 5, type.encode(Date.new(1970, 1, 6))
36
+ assert_equal 0, type.encode(Date.new(1970, 1, 1))
37
+ assert_equal(-5, type.encode(Date.new(1969, 12, 27)))
38
+
39
+ assert_equal Date.new(1970, 1, 6), type.decode(5)
40
+ assert_equal Date.new(1970, 1, 1), type.decode(0)
41
+ assert_equal Date.new(1969, 12, 27), type.decode(-5)
42
+ end
43
+
44
+ def test_timestamp_millis_long
45
+ schema = Avro::Schema.parse <<-SCHEMA
46
+ { "type": "long", "logicalType": "timestamp-millis" }
47
+ SCHEMA
48
+
49
+ # The Time.at format is (seconds, microseconds) since Epoch.
50
+ time = Time.at(628232400, 12000)
51
+
52
+ assert_equal 'timestamp-millis', schema.logical_type
53
+ assert_encode_and_decode time, schema
54
+ assert_preencoded Avro::LogicalTypes::TimestampMillis.encode(time), schema, time.utc
55
+ end
56
+
57
+ def test_timestamp_millis_long_conversion
58
+ type = Avro::LogicalTypes::TimestampMillis
59
+
60
+ now = Time.now.utc
61
+ now_millis = Time.utc(now.year, now.month, now.day, now.hour, now.min, now.sec, now.usec / 1000 * 1000)
62
+
63
+ assert_equal now_millis, type.decode(type.encode(now_millis))
64
+ assert_equal 1432849613221, type.encode(Time.utc(2015, 5, 28, 21, 46, 53, 221000))
65
+ assert_equal 1432849613221, type.encode(DateTime.new(2015, 5, 28, 21, 46, 53.221))
66
+ assert_equal Time.utc(2015, 5, 28, 21, 46, 53, 221000), type.decode(1432849613221)
67
+ end
68
+
69
+ def test_timestamp_micros_long
70
+ schema = Avro::Schema.parse <<-SCHEMA
71
+ { "type": "long", "logicalType": "timestamp-micros" }
72
+ SCHEMA
73
+
74
+ # The Time.at format is (seconds, microseconds) since Epoch.
75
+ time = Time.at(628232400, 12345)
76
+
77
+ assert_equal 'timestamp-micros', schema.logical_type
78
+ assert_encode_and_decode time, schema
79
+ assert_preencoded Avro::LogicalTypes::TimestampMicros.encode(time), schema, time.utc
80
+ end
81
+
82
+ def test_timestamp_micros_long_conversion
83
+ type = Avro::LogicalTypes::TimestampMicros
84
+
85
+ now = Time.now.utc
86
+
87
+ assert_equal Time.utc(now.year, now.month, now.day, now.hour, now.min, now.sec, now.usec), type.decode(type.encode(now))
88
+ assert_equal 1432849613221843, type.encode(Time.utc(2015, 5, 28, 21, 46, 53, 221843))
89
+ assert_equal 1432849613221843, type.encode(DateTime.new(2015, 5, 28, 21, 46, 53.221843))
90
+ assert_equal Time.utc(2015, 5, 28, 21, 46, 53, 221843), type.decode(1432849613221843)
91
+ end
92
+
93
+ def test_parse_fixed_duration
94
+ schema = Avro::Schema.parse <<-SCHEMA
95
+ { "type": "fixed", "size": 12, "name": "fixed_dur", "logicalType": "duration" }
96
+ SCHEMA
97
+
98
+ assert_equal 'duration', schema.logical_type
99
+ end
100
+
101
+ def encode(datum, schema)
102
+ buffer = StringIO.new("")
103
+ encoder = Avro::IO::BinaryEncoder.new(buffer)
104
+
105
+ datum_writer = Avro::IO::DatumWriter.new(schema)
106
+ datum_writer.write(datum, encoder)
107
+
108
+ buffer.string
109
+ end
110
+
111
+ def decode(encoded, schema)
112
+ buffer = StringIO.new(encoded)
113
+ decoder = Avro::IO::BinaryDecoder.new(buffer)
114
+
115
+ datum_reader = Avro::IO::DatumReader.new(schema, schema)
116
+ datum_reader.read(decoder)
117
+ end
118
+
119
+ def assert_encode_and_decode(datum, schema)
120
+ encoded = encode(datum, schema)
121
+ assert_equal datum, decode(encoded, schema)
122
+ end
123
+
124
+ def assert_preencoded(datum, schema, decoded)
125
+ encoded = encode(datum, schema)
126
+ assert_equal decoded, decode(encoded, schema)
127
+ end
128
+ end