/* * 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.sdk.io.range; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; 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 ByteKeyRangeTracker}. */ @RunWith(JUnit4.class) public class ByteKeyRangeTrackerTest { private static final ByteKey BEFORE_START_KEY = ByteKey.of(0x11); private static final ByteKey INITIAL_START_KEY = ByteKey.of(0x12); private static final ByteKey AFTER_START_KEY = ByteKey.of(0x13); private static final ByteKey INITIAL_MIDDLE_KEY = ByteKey.of(0x23); private static final ByteKey NEW_START_KEY = ByteKey.of(0x14); private static final ByteKey NEW_MIDDLE_KEY = ByteKey.of(0x24); private static final ByteKey BEFORE_END_KEY = ByteKey.of(0x33); private static final ByteKey END_KEY = ByteKey.of(0x34); private static final double INITIAL_RANGE_SIZE = 0x34 - 0x12; private static final ByteKeyRange INITIAL_RANGE = ByteKeyRange.of(INITIAL_START_KEY, END_KEY); private static final double NEW_RANGE_SIZE = 0x34 - 0x14; private static final ByteKeyRange NEW_RANGE = ByteKeyRange.of(NEW_START_KEY, END_KEY); @Rule public final ExpectedException expected = ExpectedException.none(); /** Tests for {@link ByteKeyRangeTracker#toString}. */ @Test public void testToString() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); String expected = String.format("ByteKeyRangeTracker{range=%s, position=null}", INITIAL_RANGE); assertEquals(expected, tracker.toString()); tracker.tryReturnRecordAt(true, INITIAL_START_KEY); tracker.tryReturnRecordAt(true, INITIAL_MIDDLE_KEY); expected = String.format("ByteKeyRangeTracker{range=%s, position=%s}", INITIAL_RANGE, INITIAL_MIDDLE_KEY); assertEquals(expected, tracker.toString()); } /** Tests for updating the start key to the first record returned. */ @Test public void testUpdateStartKey() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); tracker.tryReturnRecordAt(true, NEW_START_KEY); String expected = String.format("ByteKeyRangeTracker{range=%s, position=%s}", NEW_RANGE, NEW_START_KEY); assertEquals(expected, tracker.toString()); } /** Tests for {@link ByteKeyRangeTracker#of}. */ @Test public void testBuilding() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); assertEquals(INITIAL_START_KEY, tracker.getStartPosition()); assertEquals(END_KEY, tracker.getStopPosition()); } /** Tests for {@link ByteKeyRangeTracker#getFractionConsumed()}. */ @Test public void testGetFractionConsumed() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); double delta = 0.00001; assertEquals(0.0, tracker.getFractionConsumed(), delta); tracker.tryReturnRecordAt(true, INITIAL_START_KEY); assertEquals(0.0, tracker.getFractionConsumed(), delta); tracker.tryReturnRecordAt(true, INITIAL_MIDDLE_KEY); assertEquals(0.5, tracker.getFractionConsumed(), delta); tracker.tryReturnRecordAt(true, BEFORE_END_KEY); assertEquals(1 - 1 / INITIAL_RANGE_SIZE, tracker.getFractionConsumed(), delta); } /** Tests for {@link ByteKeyRangeTracker#getFractionConsumed()} with updated start key. */ @Test public void testGetFractionConsumedUpdateStartKey() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); double delta = 0.00001; tracker.tryReturnRecordAt(true, NEW_START_KEY); assertEquals(0.0, tracker.getFractionConsumed(), delta); tracker.tryReturnRecordAt(true, NEW_MIDDLE_KEY); assertEquals(0.5, tracker.getFractionConsumed(), delta); tracker.tryReturnRecordAt(true, BEFORE_END_KEY); assertEquals(1 - 1 / NEW_RANGE_SIZE, tracker.getFractionConsumed(), delta); } /** Tests for {@link ByteKeyRangeTracker#tryReturnRecordAt}. */ @Test public void testTryReturnRecordAt() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); // Should be able to emit at the same key twice, should that happen. // Should be able to emit within range (in order, but system guarantees won't try out of order). // Should not be able to emit past end of range. assertTrue(tracker.tryReturnRecordAt(true, INITIAL_START_KEY)); assertTrue(tracker.tryReturnRecordAt(true, INITIAL_START_KEY)); assertTrue(tracker.tryReturnRecordAt(true, INITIAL_MIDDLE_KEY)); assertTrue(tracker.tryReturnRecordAt(true, INITIAL_MIDDLE_KEY)); assertTrue(tracker.tryReturnRecordAt(true, BEFORE_END_KEY)); assertFalse(tracker.tryReturnRecordAt(true, END_KEY)); // after end assertFalse(tracker.tryReturnRecordAt(true, BEFORE_END_KEY)); // false because done } @Test public void testTryReturnFirstRecordNotSplitPoint() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); expected.expect(IllegalStateException.class); tracker.tryReturnRecordAt(false, INITIAL_START_KEY); } @Test public void testTryReturnBeforeStartKey() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); expected.expect(IllegalStateException.class); tracker.tryReturnRecordAt(true, BEFORE_START_KEY); } @Test public void testTryReturnBeforeLastReturnedRecord() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); assertTrue(tracker.tryReturnRecordAt(true, INITIAL_START_KEY)); assertTrue(tracker.tryReturnRecordAt(true, INITIAL_MIDDLE_KEY)); expected.expect(IllegalStateException.class); tracker.tryReturnRecordAt(true, AFTER_START_KEY); } /** Tests for {@link ByteKeyRangeTracker#trySplitAtPosition}. */ @Test public void testSplitAtPosition() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); // Unstarted, should not split. assertFalse(tracker.trySplitAtPosition(INITIAL_MIDDLE_KEY)); // Start it, split it before the end. assertTrue(tracker.tryReturnRecordAt(true, INITIAL_START_KEY)); assertTrue(tracker.trySplitAtPosition(BEFORE_END_KEY)); assertEquals(BEFORE_END_KEY, tracker.getStopPosition()); // Should not be able to split it after the end. assertFalse(tracker.trySplitAtPosition(END_KEY)); // Should not be able to split after emitting. assertTrue(tracker.tryReturnRecordAt(true, INITIAL_MIDDLE_KEY)); assertFalse(tracker.trySplitAtPosition(INITIAL_MIDDLE_KEY)); assertTrue(tracker.tryReturnRecordAt(true, INITIAL_MIDDLE_KEY)); } /** Tests for {@link ByteKeyRangeTracker#getSplitPointsConsumed()}. */ @Test public void testGetSplitPointsConsumed() { ByteKeyRangeTracker tracker = ByteKeyRangeTracker.of(INITIAL_RANGE); assertEquals(0, tracker.getSplitPointsConsumed()); // Started, 0 split points consumed assertTrue(tracker.tryReturnRecordAt(true, INITIAL_START_KEY)); assertEquals(0, tracker.getSplitPointsConsumed()); // Processing new split point, 1 split point consumed assertTrue(tracker.tryReturnRecordAt(true, AFTER_START_KEY)); assertEquals(1, tracker.getSplitPointsConsumed()); // Processing new non-split point, 1 split point consumed assertTrue(tracker.tryReturnRecordAt(false, INITIAL_MIDDLE_KEY)); assertEquals(1, tracker.getSplitPointsConsumed()); // Processing new split point, 2 split points consumed assertTrue(tracker.tryReturnRecordAt(true, BEFORE_END_KEY)); assertEquals(2, tracker.getSplitPointsConsumed()); // Mark tracker as done, 3 split points consumed tracker.markDone(); assertEquals(3, tracker.getSplitPointsConsumed()); } }