/* * Copyright (c) 2008-2014 MongoDB, 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 org.bson; import org.bson.io.BasicOutputBuffer; import org.bson.io.ByteBufferBsonInput; import org.bson.types.ObjectId; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; public class BsonBinaryWriterTest { private BsonBinaryWriter writer; private BasicOutputBuffer buffer; @Before public void setup() { buffer = new BasicOutputBuffer(); writer = new BsonBinaryWriter(new BsonWriterSettings(100), new BsonBinaryWriterSettings(1024), buffer); } @After public void tearDown() { writer.close(); } @Test public void shouldThrowWhenMaxDocumentSizeIsExceeded() { try { writer.writeStartDocument(); writer.writeBinaryData("b", new BsonBinary(new byte[1024])); writer.writeEndDocument(); fail(); } catch (BsonSerializationException e) { assertEquals("Document size of 1037 is larger than maximum of 1024.", e.getMessage()); } } @Test public void shouldThrowIfAPushedMaxDocumentSizeIsExceeded() { try { writer.writeStartDocument(); writer.pushMaxDocumentSize(10); writer.writeStartDocument("doc"); writer.writeString("s", "123456789"); writer.writeEndDocument(); } catch (BsonSerializationException e) { assertEquals("Document size of 22 is larger than maximum of 10.", e.getMessage()); } } @Test public void shouldNotThrowIfAPoppedMaxDocumentSizeIsExceeded() { writer.writeStartDocument(); writer.pushMaxDocumentSize(10); writer.writeStartDocument("doc"); writer.writeEndDocument(); writer.popMaxDocumentSize(); writer.writeBinaryData("bin", new BsonBinary(new byte[256])); writer.writeEndDocument(); } @Test public void testWriteAndReadBoolean() { writer.writeStartDocument(); writer.writeBoolean("b1", true); writer.writeBoolean("b2", false); writer.writeEndDocument(); byte[] expectedValues = {15, 0, 0, 0, 8, 98, 49, 0, 1, 8, 98, 50, 0, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); BsonReader reader = createReaderForBytes(expectedValues); reader.readStartDocument(); assertThat(reader.readBsonType(), is(BsonType.BOOLEAN)); assertEquals("b1", reader.readName()); assertEquals(true, reader.readBoolean()); assertThat(reader.readBsonType(), is(BsonType.BOOLEAN)); assertEquals("b2", reader.readName()); assertEquals(false, reader.readBoolean()); reader.readEndDocument(); } @Test public void testWriteAndReadString() { writer.writeStartDocument(); writer.writeString("s1", ""); writer.writeString("s2", "danke"); writer.writeString("s3", ",+\\\"<>;[]{}@#$%^&*()+_"); writer.writeString("s4", "a\u00e9\u3042\u0430\u0432\u0431\u0434"); writer.writeEndDocument(); byte[] expectedValues = {82, 0, 0, 0, 2, 115, 49, 0, 1, 0, 0, 0, 0, 2, 115, 50, 0, 6, 0, 0, 0, 100, 97, 110, 107, 101, 0, 2, 115, 51, 0, 23, 0, 0, 0, 44, 43, 92, 34, 60, 62, 59, 91, 93, 123, 125, 64, 35, 36, 37, 94, 38, 42, 40, 41, 43, 95, 0, 2, 115, 52, 0, 15, 0, 0, 0, 97, -61, -87, -29, -127, -126, -48, -80, -48, -78, -48, -79, -48, -76, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); BsonReader reader = createReaderForBytes(expectedValues); reader.readStartDocument(); assertThat(reader.readBsonType(), is(BsonType.STRING)); assertEquals("s1", reader.readName()); assertEquals("", reader.readString()); assertThat(reader.readBsonType(), is(BsonType.STRING)); assertEquals("s2", reader.readName()); assertEquals("danke", reader.readString()); assertThat(reader.readBsonType(), is(BsonType.STRING)); assertEquals("s3", reader.readName()); assertEquals(",+\\\"<>;[]{}@#$%^&*()+_", reader.readString()); assertThat(reader.readBsonType(), is(BsonType.STRING)); assertEquals("s4", reader.readName()); assertEquals("a\u00e9\u3042\u0430\u0432\u0431\u0434", reader.readString()); reader.readEndDocument(); } @Test public void testWriteNumbers() { writer.writeStartDocument(); writer.writeInt32("i1", -12); writer.writeInt32("i2", Integer.MIN_VALUE); writer.writeInt64("i3", Long.MAX_VALUE); writer.writeInt64("i4", 0); writer.writeEndDocument(); byte[] expectedValues = {45, 0, 0, 0, 16, 105, 49, 0, -12, -1, -1, -1, 16, 105, 50, 0, 0, 0, 0, -128, 18, 105, 51, 0, -1, -1, -1, -1, -1, -1, -1, 127, 18, 105, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteArray() { writer.writeStartDocument(); writer.writeStartArray("a1"); writer.writeEndArray(); writer.writeStartArray("a2"); writer.writeStartArray(); writer.writeEndArray(); writer.writeEndArray(); writer.writeEndDocument(); byte[] expectedValues = {31, 0, 0, 0, 4, 97, 49, 0, 5, 0, 0, 0, 0, 4, 97, 50, 0, 13, 0, 0, 0, 4, 48, 0, 5, 0, 0, 0, 0, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteArrayElements() { writer.writeStartDocument(); writer.writeStartArray("a1"); writer.writeBoolean(true); writer.writeBoolean(false); writer.writeEndArray(); writer.writeEndDocument(); byte[] expectedValues = {22, 0, 0, 0, 4, 97, 49, 0, 13, 0, 0, 0, 8, 48, 0, 1, 8, 49, 0, 0, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteNull() { writer.writeStartDocument(); writer.writeNull("n1"); writer.writeName("n2"); writer.writeNull(); writer.writeEndDocument(); byte[] expectedValues = {13, 0, 0, 0, 10, 110, 49, 0, 10, 110, 50, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteUndefined() { writer.writeStartDocument(); writer.writeName("u1"); writer.writeUndefined(); writer.writeUndefined("u2"); writer.writeEndDocument(); byte[] expectedValues = {13, 0, 0, 0, 6, 117, 49, 0, 6, 117, 50, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteObjectId() { ObjectId id = new ObjectId("50d3332018c6a1d8d1662b61"); writer.writeStartDocument(); writer.writeObjectId("_id", id); writer.writeEndDocument(); byte[] expectedValues = {22, 0, 0, 0, 7, 95, 105, 100, 0, 80, -45, 51, 32, 24, -58, -95, -40, -47, 102, 43, 97, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteJavaScript() { writer.writeStartDocument(); writer.writeJavaScript("js1", "var i = 0"); writer.writeJavaScriptWithScope("js2", "i++"); writer.writeStartDocument(); writer.writeInt32("x", 1); writer.writeEndDocument(); writer.writeEndDocument(); byte[] expectedValues = {53, 0, 0, 0, 13, 106, 115, 49, 0, 10, 0, 0, 0, 118, 97, 114, 32, 105, 32, 61, 32, 48, 0, 15, 106, 115, 50, 0, 24, 0, 0, 0, 4, 0, 0, 0, 105, 43, 43, 0, 12, 0, 0, 0, 16, 120, 0, 1, 0, 0, 0, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteMinMaxKeys() { writer.writeStartDocument(); writer.writeMaxKey("k1"); writer.writeMinKey("k2"); writer.writeName("k3"); writer.writeMaxKey(); writer.writeEndDocument(); byte[] expectedValues = {17, 0, 0, 0, 127, 107, 49, 0, -1, 107, 50, 0, 127, 107, 51, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteBinary() { writer.writeStartDocument(); writer.writeBinaryData("b1", new BsonBinary(new byte[]{0, 0, 0, 0, 0, 0, 0, 0})); writer.writeBinaryData("b2", new BsonBinary(BsonBinarySubType.OLD_BINARY, new byte[]{1, 1, 1, 1, 1})); writer.writeBinaryData("b3", new BsonBinary(BsonBinarySubType.FUNCTION, new byte[]{})); writer.writeEndDocument(); byte[] expectedValues = {49, 0, 0, 0, 5, 98, 49, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 98, 50, 0, 9, 0, 0, 0, 2, 5, 0, 0, 0, 1, 1, 1, 1, 1, 5, 98, 51, 0, 0, 0, 0, 0, 1, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteRegularExpression() { writer.writeStartDocument(); writer.writeRegularExpression("r1", new BsonRegularExpression("([01]?[0-9][0-9]?)")); writer.writeRegularExpression("r2", new BsonRegularExpression("[ \\t]+$", "i")); writer.writeEndDocument(); byte[] expectedValues = {43, 0, 0, 0, 11, 114, 49, 0, 40, 91, 48, 49, 93, 63, 91, 48, 45, 57, 93, 91, 48, 45, 57, 93, 63, 41, 0, 0, 11, 114, 50, 0, 91, 32, 92, 116, 93, 43, 36, 0, 105, 0, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteTimestamp() { writer.writeStartDocument(); writer.writeTimestamp("t1", new BsonTimestamp(123999401, 44332)); writer.writeEndDocument(); byte[] expectedValues = {17, 0, 0, 0, 17, 116, 49, 0, 44, -83, 0, 0, -87, 20, 100, 7, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); } @Test public void testWriteDBPointer() { writer.writeStartDocument(); BsonDbPointer dbPointer = new BsonDbPointer("my.test", new ObjectId("50d3332018c6a1d8d1662b61")); writer.writeDBPointer("pt", dbPointer); writer.writeEndDocument(); byte[] expectedValues = {33, 0, 0, 0, 12, 112, 116, 0, 8, 0, 0, 0, 109, 121, 46, 116, 101, 115, 116, 0, 80, -45, 51, 32, 24, -58, -95, -40, -47, 102, 43, 97, 0}; assertArrayEquals(expectedValues, buffer.toByteArray()); BsonReader reader = createReaderForBytes(expectedValues); reader.readStartDocument(); assertThat(reader.readBsonType(), is(BsonType.DB_POINTER)); assertEquals("pt", reader.readName()); assertEquals(dbPointer, reader.readDBPointer()); reader.readEndDocument(); } @Test //CHECKSTYLE:OFF public void testWriteRead() throws IOException { ObjectId oid1 = new ObjectId(); writer.writeStartDocument(); { writer.writeBoolean("b1", true); writer.writeBoolean("b2", false); writer.writeStartArray("a1"); { writer.writeString("danke"); writer.writeString(""); } writer.writeEndArray(); writer.writeStartDocument("d1"); { writer.writeDouble("do", 60); writer.writeInt32("i32", 40); writer.writeInt64("i64", Long.MAX_VALUE); } writer.writeEndDocument(); writer.writeJavaScriptWithScope("js1", "print x"); writer.writeStartDocument(); { writer.writeInt32("x", 1); } writer.writeEndDocument(); writer.writeObjectId("oid1", oid1); } writer.writeEndDocument(); assertEquals(139, buffer.getPosition()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); buffer.pipe(baos); ByteBufferBsonInput basicInputBuffer = new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(baos.toByteArray()))); BsonBinaryReader reader = new BsonBinaryReader(basicInputBuffer); try { assertEquals(BsonType.DOCUMENT, reader.readBsonType()); reader.readStartDocument(); { assertEquals(BsonType.BOOLEAN, reader.readBsonType()); assertEquals("b1", reader.readName()); assertEquals(true, reader.readBoolean()); assertEquals(BsonType.BOOLEAN, reader.readBsonType()); assertEquals("b2", reader.readName()); assertEquals(false, reader.readBoolean()); assertEquals(BsonType.ARRAY, reader.readBsonType()); assertEquals("a1", reader.readName()); reader.readStartArray(); { assertEquals(BsonType.STRING, reader.readBsonType()); assertEquals("danke", reader.readString()); assertEquals(BsonType.STRING, reader.readBsonType()); assertEquals("", reader.readString()); } assertEquals(BsonType.END_OF_DOCUMENT, reader.readBsonType()); reader.readEndArray(); assertEquals("d1", reader.readName()); reader.readStartDocument(); { assertEquals(BsonType.DOUBLE, reader.readBsonType()); assertEquals("do", reader.readName()); assertEquals(60, reader.readDouble(), 0); assertEquals(BsonType.INT32, reader.readBsonType()); assertEquals("i32", reader.readName()); assertEquals(40, reader.readInt32()); assertEquals(BsonType.INT64, reader.readBsonType()); assertEquals("i64", reader.readName()); assertEquals(Long.MAX_VALUE, reader.readInt64()); } assertEquals(BsonType.END_OF_DOCUMENT, reader.readBsonType()); reader.readEndDocument(); assertEquals(BsonType.JAVASCRIPT_WITH_SCOPE, reader.readBsonType()); assertEquals("js1", reader.readName()); assertEquals("print x", reader.readJavaScriptWithScope()); reader.readStartDocument(); { assertEquals(BsonType.INT32, reader.readBsonType()); assertEquals("x", reader.readName()); assertEquals(1, reader.readInt32()); } assertEquals(BsonType.END_OF_DOCUMENT, reader.readBsonType()); reader.readEndDocument(); assertEquals(BsonType.OBJECT_ID, reader.readBsonType()); assertEquals("oid1", reader.readName()); assertEquals(oid1, reader.readObjectId()); assertEquals(BsonType.END_OF_DOCUMENT, reader.readBsonType()); reader.readEndDocument(); } } finally { reader.close(); } } //CHECKSTYLE:ON @Test public void testPipe() { writer.writeStartDocument(); writer.writeBoolean("a", true); writer.writeEndDocument(); byte[] bytes = buffer.toByteArray(); BasicOutputBuffer newBuffer = new BasicOutputBuffer(); BsonBinaryWriter newWriter = new BsonBinaryWriter(newBuffer); try { BsonBinaryReader reader = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(bytes)))); try { newWriter.pipe(reader); } finally { reader.close(); } } finally { newWriter.close(); } assertArrayEquals(bytes, newBuffer.toByteArray()); } @Test public void testPipeNestedDocument() { // { // "value" : { "a" : true}, // "b" : 2 // } writer.writeStartDocument(); writer.writeStartDocument("value"); writer.writeBoolean("a", true); writer.writeEndDocument(); writer.writeInt32("b", 2); writer.writeEndDocument(); byte[] bytes = buffer.toByteArray(); BasicOutputBuffer newBuffer = new BasicOutputBuffer(); BsonBinaryWriter newWriter = new BsonBinaryWriter(newBuffer); BsonBinaryReader reader1 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(bytes)))); reader1.readStartDocument(); reader1.readName(); newWriter.pipe(reader1); //pipe {'a':true} to writer assertEquals(BsonType.INT32, reader1.readBsonType()); //continue reading from the same reader assertEquals("b", reader1.readName()); assertEquals(2, reader1.readInt32()); BsonBinaryReader reader2 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(newBuffer .toByteArray())))); reader2.readStartDocument(); //checking what writer piped assertEquals(BsonType.BOOLEAN, reader2.readBsonType()); assertEquals("a", reader2.readName()); assertEquals(true, reader2.readBoolean()); reader2.readEndDocument(); } @Test public void testPipeDocumentIntoArray() { writer.writeStartDocument(); writer.writeEndDocument(); byte[] bytes = buffer.toByteArray(); BasicOutputBuffer newBuffer = new BasicOutputBuffer(); BsonBinaryWriter newWriter = new BsonBinaryWriter(newBuffer); BsonBinaryReader reader1 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(bytes)))); newWriter.writeStartDocument(); newWriter.writeStartArray("a"); newWriter.pipe(reader1); newWriter.writeEndArray(); newWriter.writeEndDocument(); BsonBinaryReader reader2 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(newBuffer .toByteArray())))); //checking what writer piped reader2.readStartDocument(); reader2.readStartArray(); reader2.readStartDocument(); reader2.readEndDocument(); reader2.readEndArray(); reader2.readEndDocument(); } @Test public void testPipeDocumentIntoDocument() { writer.writeStartDocument(); writer.writeString("str", "value"); writer.writeEndDocument(); byte[] bytes = buffer.toByteArray(); BasicOutputBuffer newBuffer = new BasicOutputBuffer(); BsonBinaryWriter newWriter = new BsonBinaryWriter(newBuffer); BsonBinaryReader reader1 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(bytes)))); newWriter.writeStartDocument(); newWriter.writeName("doc"); newWriter.pipe(reader1); newWriter.writeEndDocument(); BsonBinaryReader reader2 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(newBuffer .toByteArray())))); //checking what writer piped reader2.readStartDocument(); assertEquals("doc", reader2.readName()); reader2.readStartDocument(); assertEquals("value", reader2.readString("str")); reader2.readEndDocument(); reader2.readEndDocument(); } @Test public void testPipeDocumentIntoTopLevel() { writer.writeStartDocument(); writer.writeString("str", "value"); writer.writeEndDocument(); byte[] bytes = buffer.toByteArray(); BasicOutputBuffer newBuffer = new BasicOutputBuffer(); BsonBinaryWriter newWriter = new BsonBinaryWriter(newBuffer); BsonBinaryReader reader1 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(bytes)))); newWriter.pipe(reader1); BsonBinaryReader reader2 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(newBuffer .toByteArray())))); //checking what writer piped reader2.readStartDocument(); assertEquals("value", reader2.readString("str")); reader2.readEndDocument(); } @Test public void testPipeDocumentIntoScopeDocument() { writer.writeStartDocument(); writer.writeInt32("i", 0); writer.writeEndDocument(); byte[] bytes = buffer.toByteArray(); BasicOutputBuffer newBuffer = new BasicOutputBuffer(); BsonBinaryWriter newWriter = new BsonBinaryWriter(newBuffer); BsonBinaryReader reader1 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(bytes)))); newWriter.writeStartDocument(); newWriter.writeJavaScriptWithScope("js", "i++"); newWriter.pipe(reader1); newWriter.writeEndDocument(); BsonBinaryReader reader2 = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(newBuffer .toByteArray())))); //checking what writer piped reader2.readStartDocument(); reader2.readJavaScriptWithScope("js"); reader2.readStartDocument(); assertEquals(0, reader2.readInt32("i")); reader2.readEndDocument(); reader2.readEndDocument(); } @Test public void testPipeOfDocumentWithInvalidSize() { byte[] bytes = {4, 0, 0, 0}; // minimum document size is 5; BasicOutputBuffer newBuffer = new BasicOutputBuffer(); BsonBinaryWriter newWriter = new BsonBinaryWriter(newBuffer); try { BsonBinaryReader reader = new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(bytes)))); try { newWriter.pipe(reader); fail("Pipe is expected to fail with document size is < 5"); } catch (BsonSerializationException e) { // expected } finally { reader.close(); } } finally { newWriter.close(); } } // CHECKSTYLE:OFF @Test public void testMarkAndReset() throws IOException { writer.writeStartDocument(); writer.writeStartArray("a"); { writer.writeStartDocument(); writer.writeInt32("i", 1); writer.writeEndDocument(); } writer.mark(); { writer.writeStartDocument(); writer.writeInt32("i", 2); writer.writeEndDocument(); } writer.reset(); { writer.writeStartDocument(); writer.writeInt32("i", 3); writer.writeEndDocument(); } writer.writeEndArray(); writer.writeEndDocument(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); buffer.pipe(baos); ByteBufferBsonInput basicInputBuffer = new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(baos.toByteArray()))); BsonBinaryReader reader = new BsonBinaryReader(basicInputBuffer); try { reader.readStartDocument(); reader.readName("a"); reader.readStartArray(); { reader.readStartDocument(); assertEquals(1, reader.readInt32("i")); reader.readEndDocument(); } { reader.readStartDocument(); assertEquals(3, reader.readInt32("i")); reader.readEndDocument(); } reader.readEndArray(); reader.readEndDocument(); } finally { reader.close(); } } // CHECKSTYLE:ON private BsonBinaryReader createReaderForBytes(final byte[] bytes) { return new BsonBinaryReader(new ByteBufferBsonInput(new ByteBufNIO(ByteBuffer.wrap(bytes)))); } }