/*
* Copyright 2015-present Facebook, Inc.
*
* Licensed 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.
*/
package com.facebook.buck.bser;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;
import java.nio.charset.CharacterCodingException;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.List;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@SuppressWarnings("unchecked")
public class BserDeserializerTest {
@Rule public ExpectedException thrown = ExpectedException.none();
private static final String SHORT_11FF;
private static final String INT_1122EEFF;
private static final String LONG_0000000080000000;
private static final String LONG_11223344CCDDEEFF;
private static final String REAL_0DOT123456789;
private static final Map.Entry<String, Object> FOO_MAP_ENTRY =
new SimpleImmutableEntry<String, Object>("foo", (byte) 0x23);
private static final Map.Entry<String, Object> BAR_MAP_ENTRY =
new SimpleImmutableEntry<String, Object>("bar", (byte) 0x42);
private static final Map.Entry<String, Object> BAZ_MAP_ENTRY =
new SimpleImmutableEntry<String, Object>("baz", (byte) 0xF0);
static {
if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) {
SHORT_11FF = "11FF";
INT_1122EEFF = "1122EEFF";
LONG_0000000080000000 = "0000000080000000";
LONG_11223344CCDDEEFF = "11223344CCDDEEFF";
REAL_0DOT123456789 = "3FBF9ADD3739635F";
} else {
SHORT_11FF = "FF11";
INT_1122EEFF = "FFEE2211";
LONG_0000000080000000 = "0000008000000000";
LONG_11223344CCDDEEFF = "FFEEDDCC44332211";
REAL_0DOT123456789 = "5F633937DD9ABF3F";
}
}
private static InputStream getByteStream(String base16) {
return new ByteArrayInputStream(BaseEncoding.base16().decode(base16));
}
@Test
public void deserializeEmptyArray() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
List<Object> deserialized =
(List<Object>) deserializer.deserializeBserValue(getByteStream("00010303000300"));
List<Object> expected = ImmutableList.of();
assertThat(deserialized, equalTo(expected));
}
@Test
public void deserializeEmptyArrayTwiceReturnsSameArray() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
List<Object> deserialized =
(List<Object>) deserializer.deserializeBserValue(getByteStream("00010303000300"));
List<Object> deserialized2 =
(List<Object>) deserializer.deserializeBserValue(getByteStream("00010303000300"));
assertThat(deserialized, is(sameInstance(deserialized2)));
}
@Test
public void deserializeArrayOfInt8() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
List<Object> deserialized =
(List<Object>)
deserializer.deserializeBserValue(getByteStream("000103090003030323034203F0"));
List<Object> expected = ImmutableList.<Object>of((byte) 0x23, (byte) 0x42, (byte) 0xF0);
assertThat(deserialized, equalTo(expected));
}
@Test
public void deserializeString() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
String deserialized =
(String)
deserializer.deserializeBserValue(
getByteStream("0001030E02030B68656C6C6F20776F726C64"));
String expected = "hello world";
assertThat(deserialized, equalTo(expected));
}
@Test
public void sameStringDeserializedTwiceReturnsSameInstance() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
String deserialized =
(String)
deserializer.deserializeBserValue(
getByteStream("0001030E02030B68656C6C6F20776F726C64"));
String deserialized2 =
(String)
deserializer.deserializeBserValue(
getByteStream("0001030E02030B68656C6C6F20776F726C64"));
assertThat(deserialized, is(sameInstance(deserialized2)));
}
@Test
public void deserializeEmptyMap() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Map<String, Object> deserialized =
(Map<String, Object>) deserializer.deserializeBserValue(getByteStream("00010303010300"));
Map<String, Object> expected = ImmutableMap.of();
assertThat(deserialized, equalTo(expected));
}
@Test
public void deserializeEmptyMapTwiceReturnsSameMap() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Map<String, Object> deserialized =
(Map<String, Object>) deserializer.deserializeBserValue(getByteStream("00010303010300"));
Map<String, Object> deserialized2 =
(Map<String, Object>) deserializer.deserializeBserValue(getByteStream("00010303010300"));
assertThat(deserialized, is(sameInstance(deserialized2)));
}
@Test
public void deserializeUnsortedMapOfStringToInt8() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Map<String, Object> deserialized =
(Map<String, Object>)
deserializer.deserializeBserValue(
getByteStream("0001031B010303020303666F6F0323020303626172034202030362617A03F0"));
// Make sure the result contains these entries in the order they appeared in the input.
assertThat(deserialized.entrySet(), contains(FOO_MAP_ENTRY, BAR_MAP_ENTRY, BAZ_MAP_ENTRY));
}
@Test
public void deserializeSortedMapOfStringToInt8() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.SORTED);
Map<String, Object> deserialized =
(Map<String, Object>)
deserializer.deserializeBserValue(
getByteStream("0001031B010303020303666F6F0323020303626172034202030362617A03F0"));
// Make sure the result contains these entries in sorted order.
assertThat(deserialized.entrySet(), contains(BAR_MAP_ENTRY, BAZ_MAP_ENTRY, FOO_MAP_ENTRY));
}
@Test
public void deserializeTemplate() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
List<Map<String, Object>> deserialized =
(List<Map<String, Object>>)
deserializer.deserializeBserValue(
getByteStream(
"000103280B0003020203046E616D6502030361676503030203046672656403140203"
+ "0470657465031E0C0319"));
// We have to cast to byte because otherwise Java coerces the ages to Integer
// objects which are sadly not equal to the BSER-deserialized Byte objects with the
// same value.
assertThat(
deserialized,
Matchers.<Map<String, Object>>contains(
Matchers.<Map<String, Object>>allOf(
Matchers.<String, Object>hasEntry("name", "fred"),
Matchers.<String, Object>hasEntry("age", (byte) 20)),
Matchers.<Map<String, Object>>allOf(
Matchers.<String, Object>hasEntry("name", "pete"),
Matchers.<String, Object>hasEntry("age", (byte) 30)),
Matchers.<Map<String, Object>>allOf(
Matchers.<String, Object>hasEntry("age", (byte) 25))));
}
@Test
public void deserializeInt8() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Byte deserialized = (Byte) deserializer.deserializeBserValue(getByteStream("000103020342"));
assertThat(deserialized, equalTo((byte) 0x42));
}
@Test
public void sameInt8DeserializedTwiceReturnsSameInstance() throws IOException {
// Java actually interns small integer values for us. How nice!
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Byte deserialized = (Byte) deserializer.deserializeBserValue(getByteStream("000103020342"));
Byte deserialized2 = (Byte) deserializer.deserializeBserValue(getByteStream("000103020342"));
assertThat(deserialized, is(sameInstance(deserialized2)));
}
@Test
public void deserializeInt16() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Short deserialized =
(Short) deserializer.deserializeBserValue(getByteStream("0001030304" + SHORT_11FF));
assertThat(deserialized, equalTo((short) 0x11FF));
}
@Test
public void deserializeInt32() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Integer deserialized =
(Integer) deserializer.deserializeBserValue(getByteStream("0001030505" + INT_1122EEFF));
assertThat(deserialized, equalTo(0x1122EEFF));
}
@Test
public void deserializeInt64() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Long deserialized =
(Long)
deserializer.deserializeBserValue(getByteStream("0001030906" + LONG_11223344CCDDEEFF));
assertThat(deserialized, equalTo(0x11223344CCDDEEFFL));
}
@Test
public void deserializeReal() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Double deserialized =
(Double)
deserializer.deserializeBserValue(getByteStream("0001030907" + REAL_0DOT123456789));
assertThat(deserialized, closeTo(0.123456789, 1e-6));
}
@Test
public void deserializeTrue() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Boolean deserialized = (Boolean) deserializer.deserializeBserValue(getByteStream("0001030108"));
assertThat(deserialized, is(true));
}
@Test
public void deserializeFalse() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Boolean deserialized = (Boolean) deserializer.deserializeBserValue(getByteStream("0001030109"));
assertThat(deserialized, is(false));
}
@Test
public void deserializeNull() throws IOException {
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
Object deserialized = deserializer.deserializeBserValue(getByteStream("000103010A"));
assertThat(deserialized, is(nullValue()));
}
@Test
public void throwIfSniffLengthTooShort() throws IOException {
thrown.expect(IOException.class);
thrown.expectMessage("Invalid BSER header (expected 3 bytes, got 0 bytes)");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream(""));
}
@Test
public void throwIfInvalidHeader() throws IOException {
thrown.expect(IOException.class);
thrown.expectMessage("Invalid BSER header");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("000F03"));
}
@Test
public void throwIfInvalidHeaderLengthType() throws IOException {
thrown.expect(IOException.class);
thrown.expectMessage("Unrecognized BSER header length type 7");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("000107" + REAL_0DOT123456789));
}
@Test
public void throwIfHeaderLengthIsNegative() throws IOException {
thrown.expect(IOException.class);
thrown.expectMessage("BSER length out of range (-128 < 0)");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("00010380"));
}
@Test
public void throwIfBodyLengthIsOverMaxInt() throws IOException {
thrown.expect(IOException.class);
thrown.expectMessage("BSER length out of range (2147483648 > 2147483647)");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("000106" + LONG_0000000080000000));
}
@Test
public void throwIfHeaderLengthTooShort() throws IOException {
thrown.expect(IOException.class);
thrown.expectMessage("Invalid BSER header length (expected 1 bytes, got 0 bytes)");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("000103"));
}
@Test
public void throwIfRemainingLengthTooShort() throws IOException {
thrown.expect(IOException.class);
thrown.expectMessage("Invalid BSER header (expected 1 bytes, got 0 bytes)");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("00010301"));
}
@Test
public void throwIfStringNotUTF8() throws IOException {
thrown.expect(CharacterCodingException.class);
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("00010306020303ABCDEF"));
}
@Test
public void throwIfArrayLengthTooShort() throws IOException {
thrown.expect(BserDeserializer.BserEofException.class);
thrown.expectMessage("Prematurely reached end of BSER buffer");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("000103050003020323"));
}
@Test
public void throwIfMapLengthTooShort() throws IOException {
thrown.expect(BserDeserializer.BserEofException.class);
thrown.expectMessage("Prematurely reached end of BSER buffer");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("0001030B010303020303666F6F0323"));
}
@Test
public void throwIfMapKeyNotString() throws IOException {
thrown.expect(IOException.class);
thrown.expectMessage("Unrecognized BSER object key type 3, expected string");
BserDeserializer deserializer = new BserDeserializer(BserDeserializer.KeyOrdering.UNSORTED);
deserializer.deserializeBserValue(getByteStream("0001030701030103030323"));
}
}