/* * 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.beam.runners.dataflow.util; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import com.google.common.primitives.UnsignedBytes; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.InputStream; import java.util.Arrays; import org.apache.beam.runners.dataflow.util.RandomAccessData.RandomAccessDataCoder; import org.apache.beam.sdk.coders.Coder.Context; import org.apache.beam.sdk.coders.CoderException; import org.apache.beam.sdk.testing.CoderProperties; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link RandomAccessData}. */ @RunWith(JUnit4.class) public class RandomAccessDataTest { private static final byte[] TEST_DATA_A = new byte[]{ 0x01, 0x02, 0x03 }; private static final byte[] TEST_DATA_B = new byte[]{ 0x06, 0x05, 0x04, 0x03 }; private static final byte[] TEST_DATA_C = new byte[]{ 0x06, 0x05, 0x03, 0x03 }; @Rule public ExpectedException expectedException = ExpectedException.none(); @Test public void testCoder() throws Exception { RandomAccessData streamA = new RandomAccessData(); streamA.asOutputStream().write(TEST_DATA_A); RandomAccessData streamB = new RandomAccessData(); streamB.asOutputStream().write(TEST_DATA_A); CoderProperties.coderDecodeEncodeEqual(RandomAccessDataCoder.of(), streamA); CoderProperties.coderDeterministic(RandomAccessDataCoder.of(), streamA, streamB); CoderProperties.coderConsistentWithEquals(RandomAccessDataCoder.of(), streamA, streamB); CoderProperties.coderSerializable(RandomAccessDataCoder.of()); CoderProperties.structuralValueConsistentWithEquals( RandomAccessDataCoder.of(), streamA, streamB); assertTrue(RandomAccessDataCoder.of().isRegisterByteSizeObserverCheap(streamA)); assertEquals(4, RandomAccessDataCoder.of().getEncodedElementByteSize(streamA)); } @Test public void testCoderWithPositiveInfinityIsError() throws Exception { expectedException.expect(CoderException.class); expectedException.expectMessage("Positive infinity can not be encoded"); RandomAccessDataCoder.of().encode( RandomAccessData.POSITIVE_INFINITY, new ByteArrayOutputStream(), Context.OUTER); } @Test public void testLexicographicalComparator() throws Exception { RandomAccessData streamA = new RandomAccessData(); streamA.asOutputStream().write(TEST_DATA_A); RandomAccessData streamB = new RandomAccessData(); streamB.asOutputStream().write(TEST_DATA_B); RandomAccessData streamC = new RandomAccessData(); streamC.asOutputStream().write(TEST_DATA_C); assertTrue(RandomAccessData.UNSIGNED_LEXICOGRAPHICAL_COMPARATOR.compare( streamA, streamB) < 0); assertTrue(RandomAccessData.UNSIGNED_LEXICOGRAPHICAL_COMPARATOR.compare( streamB, streamA) > 0); assertTrue(RandomAccessData.UNSIGNED_LEXICOGRAPHICAL_COMPARATOR.compare( streamB, streamB) == 0); // Check common prefix length. assertEquals(2, RandomAccessData.UNSIGNED_LEXICOGRAPHICAL_COMPARATOR.commonPrefixLength( streamB, streamC)); // Check that we honor the start offset. assertTrue(RandomAccessData.UNSIGNED_LEXICOGRAPHICAL_COMPARATOR.compare( streamB, streamC, 3) == 0); // Test positive infinity comparisons. assertTrue(RandomAccessData.UNSIGNED_LEXICOGRAPHICAL_COMPARATOR.compare( streamA, RandomAccessData.POSITIVE_INFINITY) < 0); assertTrue(RandomAccessData.UNSIGNED_LEXICOGRAPHICAL_COMPARATOR.compare( RandomAccessData.POSITIVE_INFINITY, RandomAccessData.POSITIVE_INFINITY) == 0); assertTrue(RandomAccessData.UNSIGNED_LEXICOGRAPHICAL_COMPARATOR.compare( RandomAccessData.POSITIVE_INFINITY, streamA) > 0); } @Test public void testEqualsAndHashCode() throws Exception { // Test that equality by reference works RandomAccessData streamA = new RandomAccessData(); streamA.asOutputStream().write(TEST_DATA_A); assertEquals(streamA, streamA); assertEquals(streamA.hashCode(), streamA.hashCode()); // Test different objects containing the same data are the same RandomAccessData streamACopy = new RandomAccessData(); streamACopy.asOutputStream().write(TEST_DATA_A); assertEquals(streamA, streamACopy); assertEquals(streamA.hashCode(), streamACopy.hashCode()); // Test same length streams with different data differ RandomAccessData streamB = new RandomAccessData(); streamB.asOutputStream().write(new byte[]{ 0x01, 0x02, 0x04 }); assertNotEquals(streamA, streamB); assertNotEquals(streamA.hashCode(), streamB.hashCode()); // Test different length streams differ streamB.asOutputStream().write(TEST_DATA_B); assertNotEquals(streamA, streamB); assertNotEquals(streamA.hashCode(), streamB.hashCode()); } @Test public void testResetTo() throws Exception { RandomAccessData stream = new RandomAccessData(); stream.asOutputStream().write(TEST_DATA_A); stream.resetTo(1); assertEquals(1, stream.size()); stream.asOutputStream().write(TEST_DATA_A); assertArrayEquals(new byte[]{ 0x01, 0x01, 0x02, 0x03 }, Arrays.copyOf(stream.array(), stream.size())); } @Test public void testAsInputStream() throws Exception { RandomAccessData stream = new RandomAccessData(); stream.asOutputStream().write(TEST_DATA_A); InputStream in = stream.asInputStream(1, 1); assertEquals(0x02, in.read()); assertEquals(-1, in.read()); in.close(); } @Test public void testReadFrom() throws Exception { ByteArrayInputStream bais = new ByteArrayInputStream(TEST_DATA_A); RandomAccessData stream = new RandomAccessData(); stream.readFrom(bais, 3, 2); assertArrayEquals(new byte[]{ 0x00, 0x00, 0x00, 0x01, 0x02 }, Arrays.copyOf(stream.array(), stream.size())); bais.close(); } @Test public void testWriteTo() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); RandomAccessData stream = new RandomAccessData(); stream.asOutputStream().write(TEST_DATA_B); stream.writeTo(baos, 1, 2); assertArrayEquals(new byte[]{ 0x05, 0x04 }, baos.toByteArray()); baos.close(); } @Test public void testThatRandomAccessDataGrowsWhenResettingToPositionBeyondEnd() throws Exception { RandomAccessData stream = new RandomAccessData(0); assertArrayEquals(new byte[0], stream.array()); stream.resetTo(3); // force resize assertArrayEquals(new byte[]{ 0x00, 0x00, 0x00 }, stream.array()); } @Test public void testThatRandomAccessDataGrowsWhenReading() throws Exception { RandomAccessData stream = new RandomAccessData(0); assertArrayEquals(new byte[0], stream.array()); stream.readFrom(new ByteArrayInputStream(TEST_DATA_A), 0, TEST_DATA_A.length); assertArrayEquals(TEST_DATA_A, Arrays.copyOf(stream.array(), TEST_DATA_A.length)); } @Test public void testIncrement() throws Exception { assertEquals(new RandomAccessData(new byte[]{ 0x00, 0x01 }), new RandomAccessData(new byte[]{ 0x00, 0x00 }).increment()); assertEquals(new RandomAccessData(new byte[]{ 0x01, UnsignedBytes.MAX_VALUE }), new RandomAccessData(new byte[]{ 0x00, UnsignedBytes.MAX_VALUE }).increment()); // Test for positive infinity assertSame(RandomAccessData.POSITIVE_INFINITY, new RandomAccessData(new byte[0]).increment()); assertSame(RandomAccessData.POSITIVE_INFINITY, new RandomAccessData(new byte[]{ UnsignedBytes.MAX_VALUE }).increment()); assertSame(RandomAccessData.POSITIVE_INFINITY, RandomAccessData.POSITIVE_INFINITY.increment()); } }