blob: bb368894832580355a1a1ccf32324207f5e73e72 [file] [log] [blame]
# -*- coding: utf-8 -*-
# frozen_string_literal: true
# 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
#
# https://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 'test_help'
require 'memory_profiler'
class TestLogicalTypes < Test::Unit::TestCase
def test_int_date
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "int", "logicalType": "date" }
SCHEMA
assert_equal 'date', schema.logical_type
today = Date.today
assert_encode_and_decode today, schema
assert_preencoded Avro::LogicalTypes::IntDate.encode(today), schema, today
end
def test_int_date_conversion
type = Avro::LogicalTypes::IntDate
assert_equal 5, type.encode(Date.new(1970, 1, 6))
assert_equal 0, type.encode(Date.new(1970, 1, 1))
assert_equal(-5, type.encode(Date.new(1969, 12, 27)))
assert_equal Date.new(1970, 1, 6), type.decode(5)
assert_equal Date.new(1970, 1, 1), type.decode(0)
assert_equal Date.new(1969, 12, 27), type.decode(-5)
end
def test_timestamp_millis_long
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "long", "logicalType": "timestamp-millis" }
SCHEMA
# The Time.at format is (seconds, microseconds) since Epoch.
time = Time.at(628232400, 12000)
assert_equal 'timestamp-millis', schema.logical_type
assert_encode_and_decode time, schema
assert_preencoded Avro::LogicalTypes::TimestampMillis.encode(time), schema, time.utc
end
def test_timestamp_millis_long_conversion
type = Avro::LogicalTypes::TimestampMillis
now = Time.now.utc
now_millis = Time.utc(now.year, now.month, now.day, now.hour, now.min, now.sec, now.usec / 1000 * 1000)
assert_equal now_millis, type.decode(type.encode(now_millis))
assert_equal 1432849613221, type.encode(Time.utc(2015, 5, 28, 21, 46, 53, 221000))
assert_equal 1432849613221, type.encode(DateTime.new(2015, 5, 28, 21, 46, 53.221))
assert_equal Time.utc(2015, 5, 28, 21, 46, 53, 221000), type.decode(1432849613221)
end
def test_timestamp_micros_long
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "long", "logicalType": "timestamp-micros" }
SCHEMA
# The Time.at format is (seconds, microseconds) since Epoch.
time = Time.at(628232400, 12345)
assert_equal 'timestamp-micros', schema.logical_type
assert_encode_and_decode time, schema
assert_preencoded Avro::LogicalTypes::TimestampMicros.encode(time), schema, time.utc
end
def test_timestamp_micros_long_conversion
type = Avro::LogicalTypes::TimestampMicros
now = Time.now.utc
assert_equal Time.utc(now.year, now.month, now.day, now.hour, now.min, now.sec, now.usec), type.decode(type.encode(now))
assert_equal 1432849613221843, type.encode(Time.utc(2015, 5, 28, 21, 46, 53, 221843))
assert_equal 1432849613221843, type.encode(DateTime.new(2015, 5, 28, 21, 46, 53.221843))
assert_equal Time.utc(2015, 5, 28, 21, 46, 53, 221843), type.decode(1432849613221843)
end
def test_parse_fixed_duration
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "fixed", "size": 12, "name": "fixed_dur", "logicalType": "duration" }
SCHEMA
assert_equal 'duration', schema.logical_type
end
def test_bytes_decimal
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "bytes", "logicalType": "decimal", "precision": 9, "scale": 6 }
SCHEMA
assert_equal 'decimal', schema.logical_type
assert_equal 9, schema.precision
assert_equal 6, schema.scale
assert_encode_and_decode BigDecimal('-3.4562'), schema
assert_encode_and_decode BigDecimal('3.4562'), schema
assert_encode_and_decode 15.123, schema
assert_encode_and_decode 15, schema
assert_encode_and_decode BigDecimal('0.123456'), schema
assert_encode_and_decode BigDecimal('0'), schema
assert_encode_and_decode BigDecimal('1'), schema
assert_encode_and_decode BigDecimal('-1'), schema
assert_raise ArgumentError do
type = Avro::LogicalTypes::BytesDecimal.new(schema)
type.encode('1.23')
end
end
def test_bytes_decimal_range_errors
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2 }
SCHEMA
type = Avro::LogicalTypes::BytesDecimal.new(schema)
assert_raises RangeError do
type.encode(BigDecimal('345'))
end
assert_raises RangeError do
type.encode(BigDecimal('1.5342'))
end
assert_raises RangeError do
type.encode(BigDecimal('-1.5342'))
end
assert_raises RangeError do
type.encode(BigDecimal('-100.2'))
end
assert_raises RangeError do
type.encode(BigDecimal('-99.991'))
end
end
def test_bytes_decimal_conversion
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "bytes", "logicalType": "decimal", "precision": 12, "scale": 6 }
SCHEMA
type = Avro::LogicalTypes::BytesDecimal.new(schema)
enc = "\xcb\x43\x38".dup.force_encoding('BINARY')
assert_equal enc, type.encode(BigDecimal('-3.4562'))
assert_equal BigDecimal('-3.4562'), type.decode(enc)
assert_equal "\x34\xbc\xc8".dup.force_encoding('BINARY'), type.encode(BigDecimal('3.4562'))
assert_equal BigDecimal('3.4562'), type.decode("\x34\xbc\xc8".dup.force_encoding('BINARY'))
assert_equal "\x6a\x33\x0e\x87\x00".dup.force_encoding('BINARY'), type.encode(BigDecimal('456123.123456'))
assert_equal BigDecimal('456123.123456'), type.decode("\x6a\x33\x0e\x87\x00".dup.force_encoding('BINARY'))
end
def test_logical_type_with_schema
exception = assert_raises(ArgumentError) do
Avro::LogicalTypes::LogicalTypeWithSchema.new(nil)
end
assert_equal exception.to_s, 'schema is required'
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "bytes", "logicalType": "decimal", "precision": 12, "scale": 6 }
SCHEMA
assert_nothing_raised do
Avro::LogicalTypes::LogicalTypeWithSchema.new(schema)
end
assert_raises NotImplementedError do
Avro::LogicalTypes::LogicalTypeWithSchema.new(schema).encode(BigDecimal('2'))
end
assert_raises NotImplementedError do
Avro::LogicalTypes::LogicalTypeWithSchema.new(schema).decode('foo')
end
end
def test_bytes_decimal_object_allocations_encode
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2 }
SCHEMA
type = Avro::LogicalTypes::BytesDecimal.new(schema)
positive_value = BigDecimal('5.2')
negative_value = BigDecimal('-5.2')
[positive_value, negative_value].each do |value|
report = MemoryProfiler.report do
type.encode(value)
end
assert_equal 5, report.total_allocated
# Ruby 2.7 does not retain anything. Ruby 2.6 retains 1
assert_operator 1, :>=, report.total_retained
end
end
def test_bytes_decimal_object_allocations_decode
schema = Avro::Schema.parse <<-SCHEMA
{ "type": "bytes", "logicalType": "decimal", "precision": 4, "scale": 2 }
SCHEMA
type = Avro::LogicalTypes::BytesDecimal.new(schema)
positive_enc = "\x02\b".dup.force_encoding('BINARY')
negative_enc = "\xFD\xF8".dup.force_encoding('BINARY')
[positive_enc, negative_enc].each do |encoded|
report = MemoryProfiler.report do
type.decode(encoded)
end
assert_equal 5, report.total_allocated
# Ruby 2.7 does not retain anything. Ruby 2.6 retains 1 or 2
assert_operator 2, :>=, report.total_retained
end
end
def encode(datum, schema)
buffer = StringIO.new
encoder = Avro::IO::BinaryEncoder.new(buffer)
datum_writer = Avro::IO::DatumWriter.new(schema)
datum_writer.write(datum, encoder)
buffer.string
end
def decode(encoded, schema)
buffer = StringIO.new(encoded)
decoder = Avro::IO::BinaryDecoder.new(buffer)
datum_reader = Avro::IO::DatumReader.new(schema, schema)
datum_reader.read(decoder)
end
def assert_encode_and_decode(datum, schema)
encoded = encode(datum, schema)
assert_equal datum, decode(encoded, schema)
end
def assert_preencoded(datum, schema, decoded)
encoded = encode(datum, schema)
assert_equal decoded, decode(encoded, schema)
end
end