blob: 024534f64583e06ef598535bcc9ecd9c4c0e18f0 [file] [log] [blame]
/*
* 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.
*/
package org.apache.qpid.proton.codec;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.UUID;
import org.apache.qpid.proton.amqp.Symbol;
import org.junit.Test;
import org.mockito.Mockito;
/**
* Test decoding of AMQP Array types
*/
public class ArrayTypeCodecTest extends CodecTestSupport {
private final int LARGE_ARRAY_SIZE = 2048;
private final int SMALL_ARRAY_SIZE = 32;
@Test
public void testArrayOfPrimitiveBooleanObjects() throws IOException {
final int size = 10;
boolean[] source = new boolean[size];
for (int i = 0; i < size; ++i) {
source[i] = i % 2 == 0;
}
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject(buffer);
assertNotNull(result);
assertTrue(result.getClass().isArray());
assertTrue(result.getClass().getComponentType().isPrimitive());
boolean[] array = (boolean[]) result;
assertEquals(size, array.length);
for (int i = 0; i < size; ++i) {
assertEquals(source[i], array[i]);
}
}
@Test
public void testZeroSizedArrayOfPrimitiveBooleanObjects() throws IOException {
boolean[] source = new boolean[0];
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject();
assertNotNull(result);
assertTrue(result.getClass().isArray());
assertTrue(result.getClass().getComponentType().isPrimitive());
boolean[] array = (boolean[]) result;
assertEquals(source.length, array.length);
}
@Test
public void testArrayOfBooleanObjects() throws IOException {
final int size = 10;
Boolean[] source = new Boolean[size];
for (int i = 0; i < size; ++i) {
source[i] = i % 2 == 0;
}
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject();
assertNotNull(result);
assertTrue(result.getClass().isArray());
assertTrue(result.getClass().getComponentType().isPrimitive());
boolean[] array = (boolean[]) result;
assertEquals(size, array.length);
for (int i = 0; i < size; ++i) {
assertEquals(source[i], array[i]);
}
}
@Test
public void testZeroSizedArrayOfBooleanObjects() throws IOException {
Boolean[] source = new Boolean[0];
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject();
assertNotNull(result);
assertTrue(result.getClass().isArray());
assertTrue(result.getClass().getComponentType().isPrimitive());
boolean[] array = (boolean[]) result;
assertEquals(source.length, array.length);
}
@Test
public void testDecodeSmallBooleanArray() throws IOException {
doTestDecodeBooleanArrayType(SMALL_ARRAY_SIZE);
}
@Test
public void testDecodeLargeBooleanArray() throws IOException {
doTestDecodeBooleanArrayType(LARGE_ARRAY_SIZE);
}
private void doTestDecodeBooleanArrayType(int size) throws IOException {
boolean[] source = new boolean[size];
for (int i = 0; i < size; ++i) {
source[i] = i % 2 == 0;
}
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject();
assertNotNull(result);
assertTrue(result.getClass().isArray());
assertTrue(result.getClass().getComponentType().isPrimitive());
boolean[] array = (boolean[]) result;
assertEquals(size, array.length);
for (int i = 0; i < size; ++i) {
assertEquals(source[i], array[i]);
}
}
@Test
public void testDecodeSmallSymbolArray() throws IOException {
doTestDecodeSymbolArrayType(SMALL_ARRAY_SIZE);
}
@Test
public void testDecodeLargeSymbolArray() throws IOException {
doTestDecodeSymbolArrayType(LARGE_ARRAY_SIZE);
}
private void doTestDecodeSymbolArrayType(int size) throws IOException {
Symbol[] source = new Symbol[size];
for (int i = 0; i < size; ++i) {
source[i] = Symbol.valueOf("test->" + i);
}
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject();
assertNotNull(result);
assertTrue(result.getClass().isArray());
Symbol[] array = (Symbol[]) result;
assertEquals(size, array.length);
for (int i = 0; i < size; ++i) {
assertEquals(source[i], array[i]);
}
}
@Test
public void testDecodeSmallUUIDArray() throws IOException {
doTestDecodeUUDIArrayType(SMALL_ARRAY_SIZE);
}
@Test
public void testDecodeLargeUUDIArray() throws IOException {
doTestDecodeUUDIArrayType(LARGE_ARRAY_SIZE);
}
private void doTestDecodeUUDIArrayType(int size) throws IOException {
UUID[] source = new UUID[size];
for (int i = 0; i < size; ++i) {
source[i] = UUID.randomUUID();
}
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject();
assertNotNull(result);
assertTrue(result.getClass().isArray());
UUID[] array = (UUID[]) result;
assertEquals(size, array.length);
for (int i = 0; i < size; ++i) {
assertEquals(source[i], array[i]);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testArrayOfListsOfUUIDs() throws IOException {
ArrayList<UUID>[] source = new ArrayList[2];
for (int i = 0; i < source.length; ++i) {
source[i] = new ArrayList<UUID>(3);
source[i].add(UUID.randomUUID());
source[i].add(UUID.randomUUID());
source[i].add(UUID.randomUUID());
}
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject();
assertNotNull(result);
assertTrue(result.getClass().isArray());
List[] list = (List[]) result;
assertEquals(source.length, list.length);
for (int i = 0; i < list.length; ++i) {
assertEquals(source[i], list[i]);
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void testArrayOfMApsOfStringToUUIDs() throws IOException {
Map<String, UUID>[] source = new LinkedHashMap[2];
for (int i = 0; i < source.length; ++i) {
source[i] = new LinkedHashMap<String, UUID>();
source[i].put("1", UUID.randomUUID());
source[i].put("2", UUID.randomUUID());
source[i].put("3", UUID.randomUUID());
}
encoder.writeArray(source);
buffer.clear();
Object result = decoder.readObject();
assertNotNull(result);
assertTrue(result.getClass().isArray());
Map[] map = (Map[]) result;
assertEquals(source.length, map.length);
for (int i = 0; i < map.length; ++i) {
assertEquals(source[i], map[i]);
}
}
@Test
public void testEncodeSmallBooleanArrayReservesSpaceForPayload() throws IOException {
doTestEncodeBooleanArrayTypeReservation(SMALL_ARRAY_SIZE);
}
@Test
public void testEncodeLargeBooleanArrayReservesSpaceForPayload() throws IOException {
doTestEncodeBooleanArrayTypeReservation(LARGE_ARRAY_SIZE);
}
private void doTestEncodeBooleanArrayTypeReservation(int size) throws IOException {
boolean[] source = new boolean[size];
for (int i = 0; i < size; ++i) {
source[i] = i % 2 == 0;
}
WritableBuffer writable = new WritableBuffer.ByteBufferWrapper(this.buffer);
WritableBuffer spy = Mockito.spy(writable);
encoder.setByteBuffer(spy);
encoder.writeArray(source);
// Check that the ArrayType tries to reserve space, actual encoding size not computed here.
Mockito.verify(spy).ensureRemaining(Mockito.anyInt());
}
@Test
public void testEncodeDecodeByteArray() throws IOException {
int count = 100;
byte[] source = createPayloadArrayBytes(count);
try {
assertEquals("Unexpected source array length", count, source.length);
int encodingWidth = count < 254 ? 1 : 4; // less than 254 and not 256, since we also need 1 byte for element count, and (in this case) 1 byte for primitive element type constructor.
int arrayPayloadSize = encodingWidth + 1 + count; // variable width for element count + byte type descriptor + number of elements
int expectedEncodedArraySize = 1 + encodingWidth + arrayPayloadSize; // array type code + variable width for array size + other encoded payload
byte[] expectedEncoding = new byte[expectedEncodedArraySize];
ByteBuffer expectedEncodingWrapper = ByteBuffer.wrap(expectedEncoding);
// Write the array encoding code, array size, and element count
if(count < 254) {
expectedEncodingWrapper.put((byte) 0xE0); // 'array8' type descriptor code
expectedEncodingWrapper.put((byte) arrayPayloadSize);
expectedEncodingWrapper.put((byte) count);
} else {
expectedEncodingWrapper.put((byte) 0xF0); // 'array32' type descriptor code
expectedEncodingWrapper.putInt(arrayPayloadSize);
expectedEncodingWrapper.putInt(count);
}
// Write the type descriptor
expectedEncodingWrapper.put((byte) 0x51); // 'byte' type descriptor code
// Write the elements
for (int i = 0; i < count; i++) {
expectedEncodingWrapper.put(source[i]);
}
assertFalse("Should have filled expected encoding array", expectedEncodingWrapper.hasRemaining());
// Now verify against the actual encoding of the array
assertEquals("Unexpected buffer position", 0, buffer.position());
encoder.writeArray(source);
assertEquals("Unexpected encoded payload length", expectedEncodedArraySize, buffer.position());
byte[] actualEncoding = new byte[expectedEncodedArraySize];
buffer.flip();
buffer.get(actualEncoding);
assertFalse("Should have drained the encoder buffer contents", buffer.hasRemaining());
assertArrayEquals("Unexpected actual array encoding", expectedEncoding, actualEncoding);
// Now verify against the decoding
buffer.flip();
Object decoded = decoder.readObject();
assertNotNull(decoded);
assertTrue(decoded.getClass().isArray());
assertTrue(decoded.getClass().getComponentType().isPrimitive());
assertEquals(byte.class, decoded.getClass().getComponentType());
assertArrayEquals("Unexpected decoding", source, (byte[]) decoded);
}
catch (Throwable t) {
System.err.println("Error during test, source array: " + Arrays.toString(source));
throw t;
}
}
private static byte[] createPayloadArrayBytes(int length) {
Random rand = new Random(System.currentTimeMillis());
byte[] payload = new byte[length];
for (int i = 0; i < length; i++) {
payload[i] = (byte) (64 + 1 + rand.nextInt(9));
}
return payload;
}
}