| /* |
| * 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.geode.internal.cache; |
| |
| import static org.assertj.core.api.Assertions.assertThat; |
| import static org.assertj.core.api.Assertions.assertThatThrownBy; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.spy; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.stream.IntStream; |
| import java.util.stream.LongStream; |
| |
| import it.unimi.dsi.fastutil.ints.IntOpenHashSet; |
| import it.unimi.dsi.fastutil.longs.LongOpenHashSet; |
| import org.junit.Test; |
| import org.mockito.stubbing.Answer; |
| |
| import org.apache.geode.internal.cache.DiskStoreImpl.OplogEntryIdSet; |
| |
| /** |
| * Tests DiskStoreImpl.OplogEntryIdSet |
| */ |
| public class OplogEntryIdSetTest { |
| |
| @Test |
| public void testBasics() { |
| OplogEntryIdSet s = new OplogEntryIdSet(); |
| |
| LongStream.range(1, 777777).forEach(i -> assertThat(s.contains(i)).isFalse()); |
| LongStream.range(1, 777777).forEach(s::add); |
| LongStream.range(1, 777777).forEach(i -> assertThat(s.contains(i)).isTrue()); |
| |
| assertThatThrownBy(() -> s.add(DiskStoreImpl.INVALID_ID)) |
| .isInstanceOf(IllegalArgumentException.class); |
| assertThat(s.contains(0)).isFalse(); |
| |
| assertThat(s.contains(0x00000000FFFFFFFFL)).isFalse(); |
| s.add(0x00000000FFFFFFFFL); |
| assertThat(s.contains(0x00000000FFFFFFFFL)).isTrue(); |
| |
| LongStream.range(0x00000000FFFFFFFFL + 1, 0x00000000FFFFFFFFL + 777777) |
| .forEach(i -> assertThat(s.contains(i)).isFalse()); |
| LongStream.range(0x00000000FFFFFFFFL + 1, 0x00000000FFFFFFFFL + 777777).forEach(s::add); |
| LongStream.range(0x00000000FFFFFFFFL + 1, 0x00000000FFFFFFFFL + 777777) |
| .forEach(i -> assertThat(s.contains(i)).isTrue()); |
| |
| LongStream.range(1, 777777).forEach(i -> assertThat(s.contains(i)).isTrue()); |
| |
| assertThat(s.contains(Long.MAX_VALUE)).isFalse(); |
| s.add(Long.MAX_VALUE); |
| assertThat(s.contains(Long.MAX_VALUE)).isTrue(); |
| assertThat(s.contains(Long.MIN_VALUE)).isFalse(); |
| s.add(Long.MIN_VALUE); |
| assertThat(s.contains(Long.MIN_VALUE)).isTrue(); |
| } |
| |
| @Test |
| public void addMethodOverflowsWhenInternalAddThrowsIllegalArgumentException() { |
| int testEntries = 1000; |
| int magicInt = testEntries + 1; |
| long magicLong = 0x00000000FFFFFFFFL + testEntries + 1; |
| |
| Answer<Void> answer = invocationOnMock -> { |
| Number value = invocationOnMock.getArgument(0); |
| if ((value.intValue() == magicInt) || (value.longValue() == magicLong)) { |
| throw new IllegalArgumentException( |
| "Too large (XXXXXXXX expected elements with load factor Y.YY)"); |
| } |
| invocationOnMock.callRealMethod(); |
| return null; |
| }; |
| |
| IntOpenHashSet intOpenHashSet = spy(IntOpenHashSet.class); |
| doAnswer(answer).when(intOpenHashSet).add(anyInt()); |
| LongOpenHashSet longOpenHashSet = spy(LongOpenHashSet.class); |
| doAnswer(answer).when(longOpenHashSet).add(anyLong()); |
| List<IntOpenHashSet> intOpenHashSets = |
| new ArrayList<>(Collections.singletonList(intOpenHashSet)); |
| List<LongOpenHashSet> longOpenHashSets = |
| new ArrayList<>(Collections.singletonList(longOpenHashSet)); |
| OplogEntryIdSet oplogEntryIdSet = new OplogEntryIdSet(intOpenHashSets, longOpenHashSets); |
| |
| // Insert some entries. |
| assertThat(intOpenHashSets).hasSize(1); |
| assertThat(longOpenHashSets).hasSize(1); |
| IntStream.range(1, testEntries).forEach(oplogEntryIdSet::add); |
| LongStream.range(0x00000000FFFFFFFFL + 1, 0x00000000FFFFFFFFL + testEntries) |
| .forEach(oplogEntryIdSet::add); |
| |
| // Insert an entry that would cause an overflow for ints and longs. |
| oplogEntryIdSet.add(magicInt); |
| oplogEntryIdSet.add(magicLong); |
| |
| // Entries should exist and no exception should be thrown (even those that caused the exception) |
| assertThat(intOpenHashSets).hasSize(2); |
| assertThat(longOpenHashSets).hasSize(2); |
| IntStream.range(1, testEntries).forEach(i -> assertThat(oplogEntryIdSet.contains(i)).isTrue()); |
| LongStream.range(0x00000000FFFFFFFFL + 1, 0x00000000FFFFFFFFL + testEntries) |
| .forEach(i -> assertThat(oplogEntryIdSet.contains(i)).isTrue()); |
| assertThat(oplogEntryIdSet.contains(magicInt)).isTrue(); |
| assertThat(oplogEntryIdSet.contains(magicLong)).isTrue(); |
| } |
| |
| @Test |
| public void sizeShouldIncludeOverflownSets() { |
| int testEntries = 1000; |
| List<IntOpenHashSet> intHashSets = new ArrayList<>(); |
| List<LongOpenHashSet> longHashSets = new ArrayList<>(); |
| |
| IntStream.range(1, testEntries + 1).forEach(value -> { |
| IntOpenHashSet intOpenHashSet = new IntOpenHashSet(); |
| intOpenHashSet.add(value); |
| intHashSets.add(intOpenHashSet); |
| }); |
| |
| LongStream.range(0x00000000FFFFFFFFL + 1, 0x00000000FFFFFFFFL + testEntries + 1) |
| .forEach(value -> { |
| LongOpenHashSet longOpenHashSet = new LongOpenHashSet(); |
| longOpenHashSet.add(value); |
| longHashSets.add(longOpenHashSet); |
| }); |
| |
| OplogEntryIdSet oplogEntryIdSet = new OplogEntryIdSet(intHashSets, longHashSets); |
| assertThat(oplogEntryIdSet.size()).isEqualTo(testEntries * 2); |
| } |
| |
| @Test |
| public void containsShouldSearchAcrossOverflownSets() { |
| int testEntries = 1000; |
| List<IntOpenHashSet> intHashSets = new ArrayList<>(); |
| List<LongOpenHashSet> longHashSets = new ArrayList<>(); |
| |
| IntStream.range(1, testEntries).forEach(value -> { |
| IntOpenHashSet intOpenHashSet = new IntOpenHashSet(); |
| intOpenHashSet.add(value); |
| intHashSets.add(intOpenHashSet); |
| }); |
| |
| LongStream.range(0x00000000FFFFFFFFL + 1, 0x00000000FFFFFFFFL + testEntries).forEach(value -> { |
| LongOpenHashSet longOpenHashSet = new LongOpenHashSet(); |
| longOpenHashSet.add(value); |
| longHashSets.add(longOpenHashSet); |
| }); |
| |
| OplogEntryIdSet oplogEntryIdSet = new OplogEntryIdSet(intHashSets, longHashSets); |
| |
| // All entries should be searchable across overflown sets |
| IntStream.range(1, testEntries).forEach(i -> assertThat(oplogEntryIdSet.contains(i)).isTrue()); |
| LongStream.range(0x00000000FFFFFFFFL + 1, 0x00000000FFFFFFFFL + testEntries) |
| .forEach(i -> assertThat(oplogEntryIdSet.contains(i)).isTrue()); |
| } |
| } |