Add ByteBuf leak detection @Rule

This commit introduces a JUnit rule that detects ByteBuf leaks in
subclasses of AbstractDataBufferAllocatingTestCase.
This commit is contained in:
Arjen Poutsma 2017-06-30 10:33:29 +02:00
parent b33d8c6ade
commit 621df7c978
5 changed files with 56 additions and 12 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -49,6 +49,12 @@ public class NettyDataBufferFactory implements DataBufferFactory {
this.byteBufAllocator = byteBufAllocator;
}
/**
* Return the {@code ByteBufAllocator} used by this factory.
*/
public ByteBufAllocator getByteBufAllocator() {
return this.byteBufAllocator;
}
@Override
public NettyDataBuffer allocateBuffer() {

View File

@ -28,9 +28,7 @@ import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.util.MimeTypeUtils;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
/**
* @author Sebastien Deleuze
@ -59,5 +57,7 @@ public class DataBufferDecoderTests extends AbstractDataBufferAllocatingTestCase
null, Collections.emptyMap());
assertSame(source, output);
release(fooBuffer, barBuffer);
}
}

View File

@ -28,9 +28,7 @@ import org.springframework.core.io.buffer.AbstractDataBufferAllocatingTestCase;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.util.MimeTypeUtils;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;
/**
* @author Sebastien Deleuze
@ -63,6 +61,8 @@ public class DataBufferEncoderTests extends AbstractDataBufferAllocatingTestCase
null, Collections.emptyMap());
assertSame(source, output);
release(fooBuffer, barBuffer);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,16 +18,22 @@ package org.springframework.core.io.buffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PoolArenaMetric;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocatorMetric;
import io.netty.buffer.UnpooledByteBufAllocator;
import org.junit.Rule;
import org.junit.rules.Verifier;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
/**
* @author Arjen Poutsma
@ -43,14 +49,18 @@ public abstract class AbstractDataBufferAllocatingTestCase {
return new Object[][] {
{new NettyDataBufferFactory(new UnpooledByteBufAllocator(true))},
{new NettyDataBufferFactory(new UnpooledByteBufAllocator(false))},
{new NettyDataBufferFactory(new PooledByteBufAllocator(true))},
{new NettyDataBufferFactory(new PooledByteBufAllocator(false))},
// disable caching for reliable leak detection, see https://github.com/netty/netty/issues/5275
{new NettyDataBufferFactory(new PooledByteBufAllocator(true, 1, 1, 8192, 11, 0, 0, 0, true))},
{new NettyDataBufferFactory(new PooledByteBufAllocator(false, 1, 1, 8192, 11, 0, 0, 0, true))},
{new DefaultDataBufferFactory(true)},
{new DefaultDataBufferFactory(false)}
};
}
@Rule
public final Verifier leakDetector = new LeakDetector();
protected DataBuffer createDataBuffer(int capacity) {
return this.bufferFactory.allocateBuffer(capacity);
}
@ -75,4 +85,30 @@ public abstract class AbstractDataBufferAllocatingTestCase {
};
}
private class LeakDetector extends Verifier {
@Override
protected void verify() throws Throwable {
if (bufferFactory instanceof NettyDataBufferFactory) {
ByteBufAllocator byteBufAllocator =
((NettyDataBufferFactory) bufferFactory).getByteBufAllocator();
if (byteBufAllocator instanceof PooledByteBufAllocator) {
PooledByteBufAllocator pooledByteBufAllocator =
(PooledByteBufAllocator) byteBufAllocator;
PooledByteBufAllocatorMetric metric = pooledByteBufAllocator.metric();
long allocations = calculateAllocations(metric.directArenas()) +
calculateAllocations(metric.heapArenas());
assertTrue("ByteBuf leak detected: " + allocations +
" allocations were not released", allocations == 0);
}
}
}
private long calculateAllocations(List<PoolArenaMetric> metrics) {
return metrics.stream().mapToLong(PoolArenaMetric::numActiveAllocations).sum();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -280,6 +280,8 @@ public class DataBufferTests extends AbstractDataBufferAllocatingTestCase {
bytes = new byte[9];
buffer.read(bytes);
assertArrayEquals(" World!!!".getBytes(StandardCharsets.UTF_8), bytes);
release(buffer);
}