blob: 9d10154810ec46426bcfe4aac7ac7d8a5ad4037d [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* 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 libcore.util;
import junit.framework.TestCase;
public class NativeAllocationRegistryTest extends TestCase {
static {
System.loadLibrary("javacoretests");
}
private static class TestConfig {
public boolean useAllocator;
public boolean shareRegistry;
public TestConfig(boolean useAllocator, boolean shareRegistry) {
this.useAllocator = useAllocator;
this.shareRegistry = shareRegistry;
}
}
private static class Allocation {
public byte[] javaAllocation;
public long nativeAllocation;
}
// Verify that NativeAllocations and their referents are freed before we run
// out of space for new allocations.
private void testNativeAllocation(TestConfig config) {
Runtime.getRuntime().gc();
final long max = Runtime.getRuntime().maxMemory();
final long total = Runtime.getRuntime().totalMemory();
final int size = 1024*1024;
final int expectedMaxNumAllocations = (int)(max-total)/size;
final int numSavedAllocations = expectedMaxNumAllocations/2;
Allocation[] saved = new Allocation[numSavedAllocations];
final int nativeSize = size/2;
final int javaSize = size/2;
NativeAllocationRegistry registry = new NativeAllocationRegistry(
getNativeFinalizer(), nativeSize);
// Allocate more native allocations than will fit in memory. This should
// not throw OutOfMemoryError because the few allocations we save
// references to should easily fit.
for (int i = 0; i < expectedMaxNumAllocations * 10; i++) {
if (!config.shareRegistry) {
registry = new NativeAllocationRegistry(getNativeFinalizer(), nativeSize);
}
final Allocation alloc = new Allocation();
alloc.javaAllocation = new byte[javaSize];
if (config.useAllocator) {
NativeAllocationRegistry.Allocator allocator
= new NativeAllocationRegistry.Allocator() {
public long allocate() {
alloc.nativeAllocation = doNativeAllocation(nativeSize);
return alloc.nativeAllocation;
}
};
registry.registerNativeAllocation(alloc, allocator);
} else {
alloc.nativeAllocation = doNativeAllocation(nativeSize);
registry.registerNativeAllocation(alloc, alloc.nativeAllocation);
}
saved[i%numSavedAllocations] = alloc;
}
// Verify most of the allocations have been freed.
assertTrue(getNumAllocations() < expectedMaxNumAllocations * 2);
}
public void testNativeAllocationAllocatorAndSharedRegistry() {
testNativeAllocation(new TestConfig(true, true));
}
public void testNativeAllocationNoAllocatorAndSharedRegistry() {
testNativeAllocation(new TestConfig(false, true));
}
public void testNativeAllocationAllocatorAndNoSharedRegistry() {
testNativeAllocation(new TestConfig(true, false));
}
public void testNativeAllocationNoAllocatorAndNoSharedRegistry() {
testNativeAllocation(new TestConfig(false, false));
}
public void testBadSize() {
assertThrowsIllegalArgumentException(new Runnable() {
public void run() {
NativeAllocationRegistry registry = new NativeAllocationRegistry(
getNativeFinalizer(), -8);
}
});
}
public void testNullArguments() {
final long size = 1024*1024;
final NativeAllocationRegistry registry = new NativeAllocationRegistry(
getNativeFinalizer(), size);
final long nativePtr = doNativeAllocation(size);
final Object referent = new Object();
long initialNumAllocations = getNumAllocations();
// referent should not be null
assertThrowsIllegalArgumentException(new Runnable() {
public void run() {
registry.registerNativeAllocation(null, nativePtr);
}
});
assertEquals(initialNumAllocations, getNumAllocations());
// nativePtr should not be null
assertThrowsIllegalArgumentException(new Runnable() {
public void run() {
registry.registerNativeAllocation(referent, 0);
}
});
assertEquals(initialNumAllocations, getNumAllocations());
// referent should not be null
assertThrowsIllegalArgumentException(new Runnable() {
public void run() {
registry.registerNativeAllocation(null,
new NativeAllocationRegistry.Allocator() {
public long allocate() {
return doNativeAllocation(size);
}
});
}
});
assertEquals(initialNumAllocations, getNumAllocations());
// Allocation that returns null should have no effect.
assertNull(registry.registerNativeAllocation(referent,
new NativeAllocationRegistry.Allocator() {
public long allocate() {
return 0;
}
}));
assertEquals(initialNumAllocations, getNumAllocations());
registry.applyFreeFunction(getNativeFinalizer(), nativePtr);
assertEquals(initialNumAllocations-1, getNumAllocations());
}
private static void assertThrowsIllegalArgumentException(Runnable runnable) {
try {
runnable.run();
} catch (IllegalArgumentException ex) {
return;
}
fail("Expected IllegalArgumentException, but no exception was thrown.");
}
private static native long getNativeFinalizer();
private static native long doNativeAllocation(long size);
private static native long getNumAllocations();
}