| /* |
| * 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.offheap; |
| |
| import static org.junit.Assert.fail; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.when; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.PrintStream; |
| import java.nio.ByteBuffer; |
| |
| import org.assertj.core.api.JUnitSoftAssertions; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.contrib.java.lang.system.ProvideSystemProperty; |
| import org.junit.contrib.java.lang.system.RestoreSystemProperties; |
| import org.junit.rules.ExpectedException; |
| |
| import org.apache.geode.cache.CacheClosedException; |
| import org.apache.geode.internal.cache.EntryEventImpl; |
| import org.apache.geode.internal.offheap.MemoryBlock.State; |
| import org.apache.geode.internal.serialization.DSCODE; |
| import org.apache.geode.util.internal.GeodeGlossary; |
| |
| public class MemoryBlockNodeJUnitTest { |
| |
| private MemoryAllocatorImpl ma; |
| private OutOfOffHeapMemoryListener ooohml; |
| private OffHeapMemoryStats stats; |
| private final Slab[] slabs = {new SlabImpl((int) OffHeapStorage.MIN_SLAB_SIZE), |
| new SlabImpl((int) OffHeapStorage.MIN_SLAB_SIZE * 2)}; |
| private StoredObject storedObject = null; |
| |
| @Rule |
| public final ProvideSystemProperty myPropertyHasMyValue = new ProvideSystemProperty( |
| GeodeGlossary.GEMFIRE_PREFIX + "OFF_HEAP_DO_EXPENSIVE_VALIDATION", "true"); |
| |
| @Rule |
| public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); |
| |
| @Rule |
| public ExpectedException expectedException = ExpectedException.none(); |
| |
| @Rule |
| public JUnitSoftAssertions softly = new JUnitSoftAssertions(); |
| |
| @Before |
| public void setUp() { |
| ooohml = mock(OutOfOffHeapMemoryListener.class); |
| stats = mock(OffHeapMemoryStats.class); |
| ma = MemoryAllocatorImpl.createForUnitTest(ooohml, stats, slabs); |
| } |
| |
| @After |
| public void tearDown() { |
| MemoryAllocatorImpl.freeOffHeapMemory(); |
| } |
| |
| private Object getValue() { |
| return Long.MAX_VALUE; |
| } |
| |
| private StoredObject createValueAsUnserializedStoredObject(Object value) { |
| StoredObject createdObject = createValueAsUnserializedStoredObject(value, false); |
| return createdObject; |
| } |
| |
| private StoredObject createValueAsUnserializedStoredObject(Object value, boolean isCompressed) { |
| byte[] valueInByteArray; |
| if (value instanceof Long) { |
| valueInByteArray = convertValueToByteArray(value); |
| } else { |
| valueInByteArray = (byte[]) value; |
| } |
| |
| boolean isSerialized = false; |
| |
| StoredObject createdObject = createChunk(valueInByteArray, isSerialized, isCompressed); |
| return createdObject; |
| } |
| |
| private byte[] convertValueToByteArray(Object value) { |
| return ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong((Long) value).array(); |
| } |
| |
| private StoredObject createChunk(byte[] v, boolean isSerialized, boolean isCompressed) { |
| StoredObject chunk = ma.allocateAndInitialize(v, isSerialized, isCompressed); |
| return chunk; |
| } |
| |
| |
| private StoredObject createValueAsSerializedStoredObject(Object value) { |
| StoredObject createdObject = createValueAsSerializedStoredObject(value, false); |
| return createdObject; |
| } |
| |
| private StoredObject createValueAsSerializedStoredObject(Object value, boolean isCompressed) { |
| byte[] valueInSerializedByteArray = EntryEventImpl.serialize(value); |
| |
| boolean isSerialized = true; |
| |
| StoredObject createdObject = |
| createChunk(valueInSerializedByteArray, isSerialized, isCompressed); |
| return createdObject; |
| } |
| |
| |
| private void allocateOffHeapDeserialized() { |
| Object regionEntryValue = getValue(); |
| storedObject = createValueAsUnserializedStoredObject(regionEntryValue); |
| } |
| |
| @Test |
| public void memoryBlockNodesConstructedWithNullBlockArgumentThrowNPEForOperations() { |
| expectedException.expect(NullPointerException.class); |
| |
| MemoryBlock mb = new MemoryBlockNode(ma, null); |
| Long addr = mb.getAddress(); |
| fail( |
| "Operations on MemoryBlockNodes with null block argument expected to throw NullPointerException "); |
| } |
| |
| @Test |
| public void zeroLengthMemoryBlockCausesAssertionErrorInConstructor() { |
| expectedException.expect(AssertionError.class); |
| |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) ma.allocate(0)); |
| softly.assertThat(mb.getBlockSize()).isEqualTo(0); |
| } |
| |
| @Test |
| public void getStateReturnsStateOfBlock() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| allocateOffHeapDeserialized(); |
| softly.assertThat(mb.getState()).isEqualTo(fragment.getState()); |
| } |
| |
| @Test |
| public void getMemoryAddressReturnsAddressOfBlock() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| softly.assertThat(mb.getAddress()).isEqualTo(fragment.getAddress()); |
| } |
| |
| @Test |
| public void getBlockSizeReturnsSizeOfBlock() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| softly.assertThat(mb.getBlockSize()).isEqualTo(fragment.getBlockSize()); |
| } |
| |
| @Test |
| public void getNextBlockOfSingleBlockReturnsNull() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| softly.assertThat(mb.getNextBlock()).isNull(); |
| } |
| |
| @Test |
| public void getSlabIdReturnsIdOfSlabBlockWasConstructedFrom() { |
| for (int i = 0; i < slabs.length; ++i) { |
| Fragment fragment = new Fragment(slabs[i].getMemoryAddress(), slabs[i].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| softly.assertThat(mb.getSlabId()).isEqualTo(i); |
| } |
| } |
| |
| @Test |
| public void getFreeListIdReturnsIdFromUnderlyingBlock() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| softly.assertThat(mb.getFreeListId()).isEqualTo(-1); |
| } |
| |
| @Test |
| public void getRefCountReturnsRefCountFromUnderlyingBlock() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| softly.assertThat(mb.getRefCount()).isEqualTo(0); |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb2 = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb2.getRefCount()).isEqualTo(1); |
| } |
| |
| @Test |
| public void getDataTypeReturnsTypeFromUnderlyingBlock() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb2 = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb2.getDataType()).isEqualTo("java.lang.Long"); |
| } |
| |
| @Test |
| public void getHashCodeReturnsCodeFromUnderlyingBlock() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb2 = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb2.hashCode()).isEqualTo(storedObject.hashCode()); |
| } |
| |
| @Test |
| public void equalsComparisonWithNonMemoryBlockNodeReturnsFalse() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb2 = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb2.equals(fragment)).isEqualTo(false); |
| } |
| |
| @Test |
| public void equalsComparisonWithAnotherMemoryBlockReturnsFalse() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb2 = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb2.equals(mb)).isEqualTo(false); |
| softly.assertThat(mb.equals(mb2)).isEqualTo(false); |
| } |
| |
| @Test |
| public void equalsComparisonToSelfReturnsTrue() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb.equals(mb)).isEqualTo(true); |
| } |
| |
| @Test |
| public void equalsComparisonWhenUnderlyingBlocksHaveSameMemoryAddressReturnsTrue() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb1 = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| MemoryBlock mb2 = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb1.equals(mb2)).isEqualTo(true); |
| } |
| |
| @Test |
| public void getDataTypeOfSerializedCompressedReturnsTypeFromUnderlyingBlock() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj, true); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb.getDataType()).isEqualTo("compressed object of size 9"); |
| } |
| |
| @Test |
| public void getDataTypeOfUnserializedNotCompressedReturnsTypeFromUnderlyingBlock() { |
| Object obj = getValue(); |
| storedObject = createValueAsUnserializedStoredObject(obj, false); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb.getDataType()).isEqualTo("byte[8]" + ""); |
| } |
| |
| @Test |
| public void getDataTypeOfUnserializedCompressedReturnsTypeFromUnderlyingBlock() { |
| |
| Object obj = getValue(); |
| storedObject = createValueAsUnserializedStoredObject(obj, true); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb.getDataType()).isEqualTo("compressed byte[8]"); |
| } |
| |
| @Test |
| public void getDataValueSerializedNotCompressedReturnsFromUnderlyingBlock() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| softly.assertThat(mb.getDataValue()).isNull(); |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb2 = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb2.getDataValue()).isEqualTo(getValue()); |
| } |
| |
| @Test |
| public void getDataValueSerializedCompressedReturnsBytesFromUnderlyingBlock() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj, true); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| byte[] storedObjectBytes = new byte[storedObject.getValueSizeInBytes()]; |
| storedObject.readDataBytes(0, storedObjectBytes); |
| softly.assertThat(mb.getDataValue()).isEqualTo(storedObjectBytes); |
| } |
| |
| @Test |
| public void getDataValueUnserializedCompressedReturnsBytesFromUnderlyingBlock() { |
| Object obj = getValue(); |
| storedObject = createValueAsUnserializedStoredObject(obj, true); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| byte[] storedObjectBytes = new byte[storedObject.getValueSizeInBytes()]; |
| storedObject.readDataBytes(0, storedObjectBytes); |
| softly.assertThat(mb.getDataValue()).isEqualTo(storedObjectBytes); |
| } |
| |
| @Test |
| public void getDataValueUnserializedNotCompressedReturnsBytesFromUnderlyingBlock() { |
| Object obj = getValue(); |
| storedObject = createValueAsUnserializedStoredObject(obj, false); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| byte[] storedObjectBytes = new byte[storedObject.getValueSizeInBytes()]; |
| storedObject.readDataBytes(0, storedObjectBytes); |
| softly.assertThat(mb.getDataValue()).isEqualTo(storedObjectBytes); |
| } |
| |
| @Test |
| public void getDataValueWithIllegalDataTypeCatchesIOException() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| storedObject.writeDataByte(0, DSCODE.ILLEGAL.toByte()); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| ByteArrayOutputStream errContent = new ByteArrayOutputStream(); |
| System.setErr(new PrintStream(errContent)); |
| softly.assertThat(mb.getDataValue()).isEqualTo("IOException:Unknown header byte: -127"); |
| } |
| |
| @Test |
| public void getDataValueCatchesCacheClosedException() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| OffHeapStoredObject spyStoredObject = spy((OffHeapStoredObject) storedObject); |
| doReturn("java.lang.Long").when(spyStoredObject).getDataType(); |
| doAnswer((m) -> { |
| throw new CacheClosedException("Unit test forced exception"); |
| }).when(spyStoredObject) |
| .getRawBytes(); |
| ByteArrayOutputStream errContent = new ByteArrayOutputStream(); |
| System.setErr(new PrintStream(errContent)); |
| MemoryBlock mb = new MemoryBlockNode(ma, spyStoredObject); |
| softly.assertThat(mb.getDataValue()) |
| .isEqualTo("CacheClosedException:Unit test forced exception"); |
| } |
| |
| @Test |
| public void getDataValueCatchesClassNotFoundException() throws Exception { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| OffHeapStoredObject spyStoredObject = spy((OffHeapStoredObject) storedObject); |
| doReturn("java.lang.Long").when(spyStoredObject).getDataType(); |
| doAnswer((m) -> { |
| throw new ClassNotFoundException(); |
| }).when(spyStoredObject).getRawBytes(); |
| ByteArrayOutputStream errContent = new ByteArrayOutputStream(); |
| System.setErr(new PrintStream(errContent)); |
| MemoryBlock mb = new MemoryBlockNode(ma, spyStoredObject); |
| softly.assertThat(mb.getDataValue()).isEqualTo("ClassNotFoundException:null"); |
| } |
| |
| @Test |
| public void toStringOfUnusedBlockReturnsStateUnusedAndTypeNA() { |
| Fragment fragment = new Fragment(slabs[0].getMemoryAddress(), slabs[0].getSize()); |
| MemoryBlock mb = new MemoryBlockNode(ma, fragment); |
| softly.assertThat(mb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=UNUSED, BlockSize=1024, SlabId=0, FreeListId=NONE, RefCount=0, isSerialized=false, isCompressed=false, DataType=N/A, DataValue=null}"); |
| } |
| |
| @Test |
| public void toStringOfAllocatedBlockReturnsStateAllocateddAndTypeOfData() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=ALLOCATED, BlockSize=\\d*, SlabId=0, FreeListId=NONE, RefCount=1, isSerialized=true, isCompressed=false, DataType=java.lang.Long, DataValue=9223372036854775807}"); |
| } |
| |
| @Test |
| public void toStringOfAllocatedBlockWithUnserializedValueReturnsByteArrayType() { |
| Object obj = getValue(); |
| storedObject = createValueAsUnserializedStoredObject(obj); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=ALLOCATED, BlockSize=\\d*, SlabId=0, FreeListId=NONE, RefCount=1, isSerialized=false, isCompressed=false, DataType=byte\\[8], DataValue=\\[127, -1, -1, -1, -1, -1, -1, -1]}"); |
| } |
| |
| @Test |
| public void toStringWithStateDeallocatedResultsInFreeListIdHuge() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| MemoryBlock spyMb = spy(mb); |
| softly.assertThat(spyMb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=ALLOCATED, BlockSize=\\d*, SlabId=0, FreeListId=NONE, RefCount=1, isSerialized=true, isCompressed=false, DataType=java.lang.Long, DataValue=9223372036854775807}"); |
| when(spyMb.getState()).thenReturn(State.DEALLOCATED); |
| when(spyMb.getRefCount()).thenReturn(0); |
| softly.assertThat(spyMb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=DEALLOCATED, BlockSize=\\d*, SlabId=0, FreeListId=HUGE, RefCount=0, isSerialized=true, isCompressed=false, DataType=java.lang.Long, DataValue=9223372036854775807}"); |
| } |
| |
| @Test |
| public void toStringWithFreeListNotMinus1() { |
| Object obj = getValue(); |
| storedObject = createValueAsSerializedStoredObject(obj); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| MemoryBlock spyMb = spy(mb); |
| softly.assertThat(spyMb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=ALLOCATED, BlockSize=\\d*, SlabId=0, FreeListId=NONE, RefCount=1, isSerialized=true, isCompressed=false, DataType=java.lang.Long, DataValue=9223372036854775807}"); |
| when(spyMb.getState()).thenReturn(State.DEALLOCATED); |
| when(spyMb.getRefCount()).thenReturn(0); |
| softly.assertThat(spyMb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=DEALLOCATED, BlockSize=\\d*, SlabId=0, FreeListId=HUGE, RefCount=0, isSerialized=true, isCompressed=false, DataType=java.lang.Long, DataValue=9223372036854775807}"); |
| when(spyMb.getFreeListId()).thenReturn(0); |
| softly.assertThat(spyMb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=DEALLOCATED, BlockSize=\\d*, SlabId=0, FreeListId=0, RefCount=0, isSerialized=true, isCompressed=false, DataType=java.lang.Long, DataValue=9223372036854775807}"); |
| } |
| |
| @Test |
| public void toStringOfAllocatedBlockWithLargeByteArrayValueShowsValueAsArraySize() { |
| Object obj = new byte[1024]; |
| storedObject = createValueAsUnserializedStoredObject(obj); |
| MemoryBlock mb = new MemoryBlockNode(ma, (MemoryBlock) storedObject); |
| softly.assertThat(mb.toString()).matches( |
| "MemoryBlock\\{MemoryAddress=\\d*, State=ALLOCATED, BlockSize=\\d*, SlabId=1, FreeListId=NONE, RefCount=1," |
| + " isSerialized=false, isCompressed=false, DataType=byte\\[1024], DataValue=<byte array of length 1024>}"); |
| } |
| } |