blob: 7d9230c026c089a9bc4f1559022ea2fe4b7b1fc8 [file] [log] [blame]
package org.apache.directmemory.memory;
/*
* 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.
*/
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import junit.framework.Assert;
import org.apache.directmemory.memory.MemoryManagerService;
import org.apache.directmemory.memory.MemoryTestUtils;
import org.apache.directmemory.memory.Pointer;
import org.junit.Test;
public abstract class AbstractMemoryManagerServiceTest
{
protected static final Random R = new Random();
protected static final int SMALL_PAYLOAD_LENGTH = 4;
protected static final byte[] SMALL_PAYLOAD = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
protected MemoryManagerService<Object> mms;
protected abstract MemoryManagerService<Object> instanciateMemoryManagerService( int bufferSize );
/**
* Test pointers allocation, when buffer size is not aligned with the size of stored objects.
* Null {@link Pointer} should be returned to allow {@link MemoryManagerService} to go to next step with allocation policy.
*/
@Test
public void testNotEnoughFreeSpace()
{
// Storing a first payload of 4 bytes, 1 byte remaining in the buffer. When storing a second 4 bytes payload, an null pointer should be returned.
final int BUFFER_SIZE = SMALL_PAYLOAD_LENGTH + 1;
mms = instanciateMemoryManagerService( BUFFER_SIZE );
Pointer<Object> pointer1 = mms.store( SMALL_PAYLOAD );
Assert.assertNotNull( pointer1 );
Assert.assertFalse( pointer1.isFree() );
Pointer<Object> pointer2 = mms.store( SMALL_PAYLOAD );
Assert.assertNull( pointer2 );
}
/**
* Ensure no byte is leaking when allocating several objects.
*/
@Test
public void testByteLeaking()
{
// Initializing 1 buffer of 10*4 bytes, should be able to allocate 10 objects of 4 bytes.
final int NUMBER_OF_OBJECTS = 10;
final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
mms = instanciateMemoryManagerService( BUFFER_SIZE );
for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
{
Pointer<Object> pointer = mms.store( SMALL_PAYLOAD );
Assert.assertNotNull( pointer );
}
Pointer<Object> pointerNull = mms.store( SMALL_PAYLOAD );
Assert.assertNull( pointerNull );
}
/**
* Ensure memory usage is reported correctly
*/
@Test
public void testReportCorrectUsedMemory()
{
// Initializing 1 buffer of 4*4 bytes, storing and freeing and storing again should report correct numbers.
final int NUMBER_OF_OBJECTS = 4;
final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
mms = instanciateMemoryManagerService( BUFFER_SIZE );
Pointer<Object> lastPointer = null;
for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
{
Pointer<Object> pointer = mms.store( SMALL_PAYLOAD );
Assert.assertNotNull( pointer );
lastPointer = pointer;
}
// Buffer is fully used.
Assert.assertEquals( BUFFER_SIZE, mms.used() );
Assert.assertNotNull( lastPointer );
mms.free( lastPointer );
Pointer<Object> pointerNotNull = mms.store( SMALL_PAYLOAD );
Assert.assertNotNull( pointerNotNull );
// Buffer again fully used.
Assert.assertEquals( BUFFER_SIZE, mms.used() );
}
/**
* Completely fill the buffer, free some pointer, reallocated the freed space, clear the buffer. The entire space should be
*/
@Test
public void testFullFillAndFreeAndClearBuffer()
{
final int NUMBER_OF_OBJECTS = 10;
final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
mms = instanciateMemoryManagerService( BUFFER_SIZE );
Pointer<Object> pointerFull = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) );
Assert.assertNotNull( pointerFull );
mms.free( pointerFull );
final int size1 = R.nextInt( BUFFER_SIZE / 2 ) + 1;
Pointer<Object> pointer1 = mms.store( MemoryTestUtils.generateRandomPayload( size1 ) );
Assert.assertNotNull( "Cannot store " + size1 + " bytes", pointer1 );
final int size2 = R.nextInt( ( BUFFER_SIZE - size1 ) / 2 ) + 1;
Pointer<Object> pointer2 = mms.store( MemoryTestUtils.generateRandomPayload( size2 ) );
Assert.assertNotNull( "Cannot store " + size2 + " bytes", pointer2 );
final int size3 = R.nextInt( ( BUFFER_SIZE - size1 - size2 ) / 2 ) + 1;
Pointer<Object> pointer3 = mms.store( MemoryTestUtils.generateRandomPayload( size3 ) );
Assert.assertNotNull( "Cannot store " + size3 + " bytes", pointer3 );
final int size4 = BUFFER_SIZE - size1 - size2 - size3;
Pointer<Object> pointer4 = mms.store( MemoryTestUtils.generateRandomPayload( size4 ) );
Assert.assertNotNull( "Cannot store " + size4 + " bytes", pointer4 );
mms.free( pointer1 );
Assert.assertTrue( pointer1.isFree() );
mms.free( pointer3 );
mms.free( pointer4 );
mms.free( pointer2 );
Assert.assertEquals( 0, mms.used() );
// As all pointers have been freeed, we should be able to reallocate the whole buffer
Pointer<Object> pointer6 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) );
Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer6 );
mms.clear();
// As all pointers have been cleared, we should be able to reallocate the whole buffer
Pointer<Object> pointer7 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) );
Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer7 );
mms.clear();
// As all pointers have been cleared, we should be able to reallocate the whole buffer
for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
{
Pointer<Object> pointer = mms.store( SMALL_PAYLOAD );
Assert.assertNotNull( pointer );
}
mms.clear();
// As all pointers have been cleared, we should be able to reallocate the whole buffer
Pointer<Object> pointer8 = mms.store( MemoryTestUtils.generateRandomPayload( BUFFER_SIZE ) );
Assert.assertNotNull( "Cannot store " + BUFFER_SIZE + " bytes", pointer8 );
mms.free( pointer8 );
// As all pointers have been cleared, we should be able to reallocate the whole buffer
for ( int i = 0; i < NUMBER_OF_OBJECTS * 10; i++ )
{
Pointer<Object> pointer = mms.store( SMALL_PAYLOAD );
Assert.assertNotNull( pointer );
mms.free( pointer );
}
// After a clear occurs, pointers allocated before the clear should be set as "free"
Assert.assertTrue( pointer6.isFree() );
Assert.assertTrue( pointer7.isFree() );
}
@Test
public void testRandomPayload()
{
final int NUMBER_OF_OBJECTS = 10;
final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
mms = instanciateMemoryManagerService( BUFFER_SIZE );
for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
{
byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
Pointer<Object> pointer = mms.store( payload );
Assert.assertNotNull( pointer );
byte[] fetchedPayload = mms.retrieve( pointer );
Assert.assertEquals( new String( payload ), new String( fetchedPayload ) );
if ( R.nextBoolean() )
{
mms.free( pointer );
}
}
mms.clear();
for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
{
byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
Pointer<Object> pointer = mms.store( payload );
Assert.assertNotNull( pointer );
byte[] fetchedPayload = mms.retrieve( pointer );
Assert.assertEquals( new String( payload ), new String( fetchedPayload ) );
if ( R.nextBoolean() )
{
mms.free( pointer );
i--;
}
}
}
@Test
public void testStoreAllocAndFree()
{
final int NUMBER_OF_OBJECTS = 100;
final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
mms = instanciateMemoryManagerService( BUFFER_SIZE );
List<Pointer<Object>> pointers = new ArrayList<Pointer<Object>>( NUMBER_OF_OBJECTS );
for ( int i = 0; i < NUMBER_OF_OBJECTS; i++ )
{
byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
Pointer<Object> pointer = mms.store( payload );
Assert.assertNotNull( pointer );
pointers.add( pointer );
byte[] fetchedPayload = mms.retrieve( pointer );
Assert.assertEquals( new String( payload ), new String( fetchedPayload ) );
}
// Free 1/4 of the pointers, from 1/4 of the address space to 1/2
for ( int i = NUMBER_OF_OBJECTS / 4; i < NUMBER_OF_OBJECTS / 2; i++ )
{
Pointer<Object> pointer = pointers.get( i );
mms.free( pointer );
}
// Should be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes
Pointer<Object> pointer1 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 );
Assert.assertNotNull( pointer1 );
int pointerToSkip = NUMBER_OF_OBJECTS / 2 + NUMBER_OF_OBJECTS / 10;
for ( int i = NUMBER_OF_OBJECTS / 2; i < NUMBER_OF_OBJECTS * 3 / 4; i++ )
{
// skip one pointer
if ( i == pointerToSkip )
{
continue;
}
Pointer<Object> pointer = pointers.get( i );
mms.free( pointer );
}
// Should NOT be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes
Pointer<Object> pointer2 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 );
Assert.assertNull( pointer2 );
// Freeing the previously skipped pointer should then merge the whole memory space
mms.free( pointers.get( pointerToSkip ) );
// Should be able to allocate NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH bytes
Pointer<Object> pointer3 = mms.allocate( Object.class, NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH, 0, 0 );
Assert.assertNotNull( pointer3 );
byte[] payload3 = MemoryTestUtils.generateRandomPayload( NUMBER_OF_OBJECTS / 4 * SMALL_PAYLOAD_LENGTH );
pointer3.getDirectBuffer().put( payload3 );
byte[] retrievePayload3 = mms.retrieve( pointer3 );
Assert.assertEquals( new String( payload3 ), new String( retrievePayload3 ) );
}
@Test
public void testUpdate()
{
final int NUMBER_OF_OBJECTS = 1;
final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
mms = instanciateMemoryManagerService( BUFFER_SIZE );
final byte[] payload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
final Pointer<Object> pointer = mms.store( payload );
Assert.assertNotNull( pointer );
Assert.assertEquals( new String( payload ), new String( mms.retrieve( pointer ) ) );
final byte[] otherPayload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH );
final Pointer<Object> otherPointer = mms.update( pointer, otherPayload );
Assert.assertNotNull( otherPointer );
Assert.assertEquals( pointer.getStart(), otherPointer.getStart() );
Assert.assertEquals( pointer.getEnd(), otherPointer.getEnd() );
Assert.assertEquals( new String( otherPayload ), new String( mms.retrieve( otherPointer ) ) );
final byte[] evenAnotherPayload = MemoryTestUtils.generateRandomPayload( SMALL_PAYLOAD_LENGTH / 2 );
final Pointer<Object> evenAnotherPointer = mms.update( otherPointer, evenAnotherPayload );
Assert.assertNotNull( evenAnotherPointer );
Assert.assertEquals( pointer.getStart(), evenAnotherPointer.getStart() );
Assert.assertEquals( pointer.getEnd(), evenAnotherPointer.getEnd() );
//Assert.assertEquals( 2, new String( mms.retrieve( evenAnotherPointer ) ).length() );
Assert.assertTrue( new String( mms.retrieve( evenAnotherPointer ) )
.startsWith( new String( evenAnotherPayload ) ) );
}
@Test
public void testAllocate()
{
final int NUMBER_OF_OBJECTS = 10;
final int BUFFER_SIZE = NUMBER_OF_OBJECTS * SMALL_PAYLOAD_LENGTH;
mms = instanciateMemoryManagerService( BUFFER_SIZE );
final byte[] payload1 = MemoryTestUtils.generateRandomPayload( 8 * SMALL_PAYLOAD_LENGTH );
final Pointer<Object> pointer1 = mms.store( payload1 );
Assert.assertNotNull( pointer1 );
Assert.assertEquals( new String( payload1 ), new String( mms.retrieve( pointer1 ) ) );
final byte[] payload2 = MemoryTestUtils.generateRandomPayload( 2 * SMALL_PAYLOAD_LENGTH );
final Pointer<Object> pointer2 = mms.store( payload2 );
Assert.assertNotNull( pointer2 );
Assert.assertEquals( new String( payload2 ), new String( mms.retrieve( pointer2 ) ) );
mms.free( pointer1 );
final byte[] payload3 = MemoryTestUtils.generateRandomPayload( 2 * SMALL_PAYLOAD_LENGTH );
final Pointer<Object> pointer3 = mms.store( payload3 );
Assert.assertNotNull( pointer3 );
Assert.assertEquals( new String( payload3 ), new String( mms.retrieve( pointer3 ) ) );
final int size1 = 4 * SMALL_PAYLOAD_LENGTH;
final byte[] allocatedPayload1 = MemoryTestUtils.generateRandomPayload( size1 );
final Pointer<Object> allocatedPointer1 = mms.allocate( Object.class, allocatedPayload1.length, -1, -1 );
Assert.assertNotNull( allocatedPointer1 );
final ByteBuffer buffer1 = allocatedPointer1.getDirectBuffer();
Assert.assertNotNull( buffer1 );
Assert.assertEquals( 0, buffer1.position() );
Assert.assertEquals( size1, buffer1.limit() );
Assert.assertEquals( size1, buffer1.capacity() );
buffer1.put( allocatedPayload1 );
Assert.assertEquals( new String( allocatedPayload1 ), new String( mms.retrieve( allocatedPointer1 ) ) );
final int size2 = 2 * SMALL_PAYLOAD_LENGTH;
final byte[] allocatedPayload2 = MemoryTestUtils.generateRandomPayload( size2 );
final Pointer<Object> allocatedPointer2 = mms.allocate( Object.class, allocatedPayload2.length, -1, -1 );
Assert.assertNotNull( allocatedPointer2 );
final ByteBuffer buffer2 = allocatedPointer2.getDirectBuffer();
Assert.assertNotNull( buffer2 );
Assert.assertEquals( size2, buffer2.limit() );
Assert.assertEquals( size2, buffer2.capacity() );
buffer2.put( allocatedPayload2 );
Assert.assertEquals( new String( allocatedPayload2 ), new String( mms.retrieve( allocatedPointer2 ) ) );
// Ensure the new allocation has not overwritten other data
Assert.assertEquals( new String( payload2 ), new String( mms.retrieve( pointer2 ) ) );
Assert.assertEquals( new String( payload3 ), new String( mms.retrieve( pointer3 ) ) );
}
}