1 /* 2 * Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"). 5 * You may not use this file except in compliance with the License. 6 * A copy of the License is located at 7 * 8 * http://aws.amazon.com/apache2.0 9 * 10 * or in the "license" file accompanying this file. This file is distributed 11 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 * express or implied. See the License for the specific language governing 13 * permissions and limitations under the License. 14 */ 15 package software.amazon.eventstream; 16 17 import org.junit.jupiter.api.Test; 18 19 import java.io.ByteArrayOutputStream; 20 import java.nio.ByteBuffer; 21 import java.util.HashMap; 22 import java.util.Iterator; 23 import java.util.LinkedHashMap; 24 import java.util.Map; 25 import java.util.Map.Entry; 26 import java.util.Random; 27 28 import static java.nio.charset.StandardCharsets.UTF_8; 29 import static java.util.Collections.emptyMap; 30 import static org.junit.jupiter.api.Assertions.assertArrayEquals; 31 import static org.junit.jupiter.api.Assertions.assertEquals; 32 import static org.junit.jupiter.api.Assertions.assertThrows; 33 34 public class MessageTest { 35 @Test emptyVector()36 public void emptyVector() { 37 Message message = new Message(emptyMap(), new byte[]{}); 38 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 39 message.encode(baos); 40 byte[] bytes = baos.toByteArray(); 41 42 byte[] expected = new byte[]{ 43 0, 0, 0, 16, // total_length 44 0, 0, 0, 0, // headers_length 45 5, -62, 72, -21, // prelude_crc 46 125, -104, -56, -1 // message_crc 47 }; 48 49 assertArrayEquals(expected, bytes); 50 51 assertEquals(message, Message.decode(ByteBuffer.wrap(expected))); 52 } 53 54 @Test appdataVector()55 public void appdataVector() { 56 byte[] bytes = new byte[]{ 57 0, 0, 0, 0x1d, // total_length 58 0, 0, 0, 0, // headers_length 59 (byte) 0xfd, 0x52, (byte) 0x8c, 0x5a, // prelude_crc 60 0x7b, 0x27, 0x66, 0x6f, 0x6f, 0x27, 0x3a, // payload 61 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 62 // 0xc3 65 39 36 63 (byte) 0xc3, 0x65, 0x39, 0x36 // message_crc 64 }; 65 66 Message message = new Message(emptyMap(), "{'foo':'bar'}".getBytes(UTF_8)); 67 68 assertEquals(message, Message.decode(ByteBuffer.wrap(bytes))); 69 } 70 71 @Test roundTripTests()72 public void roundTripTests() { 73 roundTrip(new Message(emptyMap(), new byte[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 })); 74 75 Map<String, HeaderValue> headers = new HashMap<>(); 76 headers.put(":content-type", HeaderValue.fromString("application/json")); 77 headers.put("content-encoding", HeaderValue.fromString("gzip")); 78 headers.put("request-id", HeaderValue.fromByteArray(new byte[]{ 1, 2, 3, 4, 5 })); 79 headers.put("more-stuff", HeaderValue.fromInteger(27)); 80 roundTrip(new Message(headers, new byte[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9 })); 81 } 82 83 @Test generativeTest()84 public void generativeTest() throws Exception { 85 long SEED = 8912374098123423L; 86 TestUtils utils = new TestUtils(SEED); 87 Random rand = new Random(SEED); 88 for (int i = 0; i < 10_000; i++) { 89 byte[] padding = new byte[rand.nextInt(128 * 1024)]; 90 rand.nextBytes(padding); 91 92 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 93 baos.write(padding); 94 Message message = utils.randomMessage(); 95 message.encode(baos); 96 97 int arraylen = 256 * 1024; 98 int arrayoffset = rand.nextInt(4 * 1024); 99 ByteBuffer buf = ((ByteBuffer) ByteBuffer.allocate(arraylen).position(arrayoffset)).slice(); 100 byte[] encodedBytes = baos.toByteArray(); 101 buf.put(encodedBytes); 102 buf.flip(); 103 104 buf.get(padding); 105 Message decoded = Message.decode(buf); 106 107 assertEquals(message, decoded); 108 109 // Corrupt a random byte and verify that decoding fails 110 encodedBytes[rand.nextInt(encodedBytes.length)]++; 111 assertThrows(IllegalArgumentException.class, () -> Message.decode(ByteBuffer.wrap(encodedBytes))); 112 } 113 } 114 115 @Test headerOrder()116 public void headerOrder() { 117 Map<String, HeaderValue> expectedHeaders = new LinkedHashMap<>(); 118 for (int i = 0; i < 255; i++) { 119 expectedHeaders.put(String.valueOf(i), HeaderValue.fromInteger(i)); 120 } 121 Message expected = new Message(expectedHeaders, new byte[]{}); 122 123 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 124 expected.encode(baos); 125 Message actual = Message.decode(ByteBuffer.wrap(baos.toByteArray())); 126 127 Map<String, HeaderValue> actualHeaders = actual.getHeaders(); 128 Iterator<Entry<String, HeaderValue>> expectedIter = expectedHeaders.entrySet().iterator(); 129 Iterator<Entry<String, HeaderValue>> actualIter = actualHeaders.entrySet().iterator(); 130 for (int i = 0; i < 255; i++) { 131 assertEquals(expectedIter.next(), actualIter.next()); 132 } 133 } 134 roundTrip(Message expected)135 static void roundTrip(Message expected) { 136 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 137 expected.encode(baos); 138 Message actual = Message.decode(ByteBuffer.wrap(baos.toByteArray())); 139 assertEquals(expected, actual); 140 } 141 } 142