/* * Copyright 2016 The Simple File Server Authors * * Licensed 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.sfs.block; import io.vertx.ext.unit.Async; import io.vertx.ext.unit.TestContext; import io.vertx.ext.unit.junit.VertxUnitRunner; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.sfs.TestSubscriber; import rx.functions.Func1; import static com.google.common.collect.Iterables.toArray; import static java.lang.System.out; import static org.sfs.util.VertxAssert.assertArrayEquals; import static org.sfs.util.VertxAssert.assertEquals; import static rx.Observable.just; @RunWith(VertxUnitRunner.class) public class RecyclingAllocatorTest { @Test public void testGetBytesFree(TestContext context) { int blockSize = 8 * 1024; RecyclingAllocator allocator = new RecyclingAllocator(blockSize); Async async = context.async(); just((Void) null) .map(new Func1<Void, Void>() { @Override public Void call(Void aVoid) { allocator.allocNextAvailable(1); long position = allocator.allocNextAvailable(1); allocator.allocNextAvailable(1); allocator.free(position, 1); int size = 100000; long free = allocator.getBytesFree(size); assertEquals(context, 100000 - (blockSize * 2), free); return null; } }) .subscribe(new TestSubscriber(context, async)); } @Test public void testAllocate(TestContext context) { int blockSize = 8 * 1024; RecyclingAllocator allocator = new RecyclingAllocator(blockSize); Async async = context.async(); just((Void) null) .map(aVoid -> allocator.allocNextAvailable(2)) .map(position -> { print(allocator); assertEquals(context, 0L, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(8192, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(100)) .map(position -> { print(allocator); assertEquals(context, blockSize, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(16384, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(100)) .map(position -> { print(allocator); assertEquals(context, blockSize * 2, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(24576, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .subscribe(new TestSubscriber(context, async)); } @Test public void testRecycle(TestContext context) { int blockSize = 8 * 1024; RecyclingAllocator allocator = new RecyclingAllocator(blockSize); long bytesFree = allocator.getBytesFree(100000); Async async = context.async(); just((Void) null) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, 0, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(blockSize, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - blockSize, allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, blockSize, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(blockSize * 2, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - (blockSize * 2), allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, blockSize * 2, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(blockSize * 3, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - (blockSize * 3), allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(blockSize * 2 + 1)) .map(position -> { print(allocator); assertEquals(context, blockSize * 3, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(blockSize * 6, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - (blockSize * 6), allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, blockSize * 6, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(blockSize * 7, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - (blockSize * 7), allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> { allocator.free(blockSize, 1); return null; }) .map(position -> { print(allocator); Assert.assertArrayEquals( new Range[]{ new Range(blockSize, 16383), new Range(blockSize * 7, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - (blockSize * 6), allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(2)) .map(position -> { print(allocator); assertEquals(context, blockSize, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(blockSize * 7, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - (blockSize * 7), allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, blockSize * 7, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(blockSize * 8, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - (blockSize * 8), allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, blockSize * 8, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(blockSize * 9, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); assertEquals(context, bytesFree - (blockSize * 9), allocator.getBytesFree(100000)); return (Void) null; }) .map(aVoid -> allocator.greatestFreePosition()) .map(position -> { assertEquals(context, 73728L, position.longValue()); return (Void) null; }) .subscribe(new TestSubscriber(context, async)); } @Test public void testAllocSpecific(TestContext context) { int blockSize = 8 * 1024; RecyclingAllocator allocator = new RecyclingAllocator(blockSize); Async async = context.async(); just((Void) null) .map(aVoid -> allocator.alloc(blockSize, 2)) .map(position -> { print(allocator); assertEquals(context, blockSize, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(0, blockSize - 1), new Range(blockSize * 2, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .map(aVoid -> allocator.alloc(blockSize * 10000, 2)) .map(position -> { print(allocator); assertEquals(context, blockSize * 10000, position.longValue()); assertArrayEquals( context, new Range[]{ new Range(0, blockSize - 1), new Range(blockSize * 2, 81919999), new Range(81928192, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .map(aVoid -> allocator.alloc(blockSize, 2)) .map(position -> { print(allocator); assertEquals(context, -1, position.longValue()); return (Void) null; }) .subscribe(new TestSubscriber(context, async)); } @Test public void testAllocRandomPositions(TestContext context) { int blockSize = 8 * 1024; RecyclingAllocator allocator = new RecyclingAllocator(blockSize); Async async = context.async(); just((Void) null) .map(aVoid -> allocator.allocNextAvailable(1)) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, blockSize * 1, position.longValue()); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, blockSize * 2, position.longValue()); return (Void) null; }) .map(aVoid -> allocator.allocNextAvailable(1)) .map(position -> { print(allocator); assertEquals(context, blockSize * 3, position.longValue()); return (Void) null; }) .subscribe(new TestSubscriber(context, async)); } @Test public void testAllocRandomFree(TestContext context) { int blockSize = 8 * 1024; RecyclingAllocator allocator = new RecyclingAllocator(blockSize); Async async = context.async(); just((Void) null) .map(aVoid -> allocator.allocNextAvailable(100000000)) .map(position -> { print(allocator); assertArrayEquals( context, new Range[]{new Range(100007936, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .map(aVoid -> { allocator.free(0, 10); return null; }) .map(position -> { print(allocator); assertArrayEquals( context, new Range[]{ new Range(0, 8191), new Range(100007936, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .map(aVoid -> { allocator.free(blockSize * 3, 10); return null; }) .map(position -> { print(allocator); assertArrayEquals( context, new Range[]{ new Range(0, 8191), new Range(24576, 32767), new Range(100007936, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .map(aVoid -> { allocator.free(blockSize, blockSize * 3); return null; }) .map(position -> { print(allocator); assertArrayEquals( context, new Range[]{ new Range(0, 32767), new Range(100007936, 9223372036854767615L)}, toArray(allocator.freeRanges(), Range.class)); return (Void) null; }) .subscribe(new TestSubscriber(context, async)); } @Test public void testLastAllocated(TestContext context) { int blockSize = 8 * 1024; RecyclingAllocator allocator = new RecyclingAllocator(blockSize); Async async = context.async(); just((Void) null) .map(aVoid -> allocator.allocNextAvailable(blockSize * 3)) .map(aVoid -> allocator.greatestFreePosition()) .map(position -> { print(allocator); assertEquals(context, blockSize * 3, position.longValue()); return (Void) null; }) .subscribe(new TestSubscriber(context, async)); } protected void print(RecyclingAllocator allocator) { for (Range freeRange : allocator.freeRanges()) { out.println("Range: " + freeRange); } out.println(); } }