我正在寻找一种方法来记录对java.nio.ByteBuffer
.
我只想知道正在调用哪些方法。
JMockit 可以做到这一点,但是从 1.47 版开始,一些非常聪明的人决定取消对私有方法的支持,而 1.46 版在 JDK 9 及更高版本上运行得不太好。
谁能推荐一个工具?它不一定需要是一个单元测试框架,但它应该在 Eclipse 中工作。
我至少需要支持 JDK 11(最好是 JDK 13)
仅作记录,以下是适用于 JMockit 1.46 和 JDK 1.8 的代码:
import java.nio.ByteBuffer;
import org.junit.Test;
import org.slf4j.*;
import mockit.*;
public class TestFakeByteBufferAdvice {
private static final Logger LOG = LoggerFactory.getLogger(TestFakeByteBufferAdvice.class);
public static final class FakeByteBuffer extends MockUp<ByteBuffer> {
@Mock
public Object $advice(Invocation invocation) {
LOG.info("$advice.....: {} {}", invocation.getInvokedMember(), invocation);
return invocation.proceed();
}
}
@Test
public void getFakeByteBuffer() {
final ByteBuffer real = ByteBuffer.wrap("abc".getBytes());
LOG.info("Real........: {} {}", real, real.array());
LOG.info("MockUp......: {}", new FakeByteBuffer());
final ByteBuffer fake = ByteBuffer.wrap("def".getBytes());
LOG.info("Fake........: {} {}", fake, fake.array());
}
}
好吧,我设法破解了解决方案。
首先,JMockit 不能很好地与 JDK13 配合使用,所以我将 JDK13 Http Client 向后移植到 JDK8。它是一个邪恶的黑客,但对于我的测试用例来说已经足够了。
然后我使用了一个稍旧的 JMockit 版本 (1.46),因为一些喜剧演员决定不允许任何人使用私有方法伪造类。
然后我将我想要跟踪的对象存储在一个列表中,这使得可以通过标识 (==) 比较从日志记录中排除不需要的对象。
仍然 JMockit 可能会在某些方法上崩溃(在下面的示例中,我已经将它们注释掉了),因此在异常之后抑制日志记录是一个想法。
我已将此报告给 JMockit 人员:https : //github.com/jmockit/jmockit1/issues/667
类似地,在构建测试用例以保持输出量减少时抑制日志记录是有意义的。我为此使用了 AtomicBoolean。
跟踪仍然没有完全解决它,但是在 Stacktrace 中抛出让我找到了解决方案:以下调用链正在读取我的 ByteBuffers:
sun.nio.ch.write(ByteBuffer[] srcs, int offset, int length)
sun.nio.ch.write(FileDescriptor fd, ByteBuffer src, long position, NativeDispatcher nd)
java.nio.DirectByteBuffer.put(ByteBuffer src)
DirectByteBuffer 使用了一些聪明的技巧来读取我的 ByteBuffer。
该解决方案仅适用于我的 Http Client hack,但无论如何,这里只是为了记录。也许其中一些会帮助其他人调试一些其他类:
package http.jmockit;
import java.net.*;
import java.net.http.*;
import java.net.http.HttpRequest.BodyPublisher;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.*;
import mockit.*;
public class TestHttpIdentity {
private static final Logger LOG = LoggerFactory.getLogger(TestHttpIdentity.class);
private static final AtomicBoolean LOG_ENABLED = new AtomicBoolean();
private static final List<Object> TRACKED = new ArrayList<>();
public static final class FakeByteBuffer extends MockUp<ByteBuffer> {
@Mock
public Object $advice(final Invocation invocation) {
if (TRACKED.stream().noneMatch(tracked -> tracked == invocation.getInvokedInstance())) {
return invocation.proceed();
}
if (LOG_ENABLED.get()) {
LOG .info("$advice.invokedInstance.: {}", invocation.getInvokedInstance(), "" /* (makes signature unique)*/);
LOG .info("$advice.invokedMember...: {}", invocation.getInvokedMember(), "" /* (makes signature unique)*/);
// Thread.dumpStack(); // Use Stack Trace as last measure if needs be
}
Object result = "Not available due to Exception in invocation.proceed()";
try {
/**/ result = invocation.proceed();
return result;
}
catch (final Throwable e) {
for (final Object arg : invocation.getInvokedArguments()) {
LOG.info("$advice.arg.............: {} class={}", arg, arg == null ? "?" : arg.getClass());
}
LOG .info("$advice.Result..........: {}", result);
LOG_ENABLED.set(false); // Disable Logging when JMockit fails
try {Thread.sleep(100);} catch (final InterruptedException shortDelayToSyncLoggingOutput) {}
e.printStackTrace();
throw e;
}
}
}
public static void main(final String[] args) throws Exception {
LOG.info("MockUp..................: {}", new FakeByteBuffer());
final ByteBuffer[] byteBuffers = TestBytes.asWrappedByteBuffers();
for (final ByteBuffer byteBuffer : byteBuffers) {
LOG.info("byteBuffer..............: {}", byteBuffer);
final int limit = byteBuffer.limit();
final int position = byteBuffer.position();
TRACKED.add(byteBuffer); // Track Objects via their Identity (==)
LOG.info("Test Bytes..............: {}", byteBuffers, "");
LOG.info("byteBuffer0.array().....: {}", byteBuffer.array());
LOG.info("byteBuffer0.capacity()..: {}", byteBuffer.capacity());
LOG.info("byteBuffer0.get().......: {}", byteBuffer.get());
// LOG.info("byteBuffer0.get(byte[]).: {}", byteBuffers0.get(new byte[5])); // ClassCastException
LOG.info("byteBuffer0.get(byte[]->) {}", byteBuffer.get(new byte[5], 0, 5));
LOG.info("byteBuffer0.get(0)......: {}", byteBuffer.get(0));
LOG.info("byteBuffer0.hasArray()..: {}", byteBuffer.hasArray());
LOG.info("byteBuffer0.hasRemaining: {}", byteBuffer.hasRemaining());
LOG.info("byteBuffer0.isDirect()..: {}", byteBuffer.isDirect());
LOG.info("byteBuffer0.isReadOnly(): {}", byteBuffer.isReadOnly());
LOG.info("byteBuffer0.limit().....: {}", limit);
LOG.info("byteBuffer0.limit(0)....: {}", byteBuffer.limit(limit));
LOG.info("byteBuffer0.mark(0).....: {}", byteBuffer.mark());
LOG.info("byteBuffer0.order().....: {}", byteBuffer.order());
LOG.info("byteBuffer0.position()..: {}", position);
LOG.info("byteBuffer0.position(99): {}", byteBuffer.position(99));
LOG.info("byteBuffer0.remaining().: {}", byteBuffer.remaining());
// LOG.info("byteBuffer0.reset().....: {}", byteBuffers0.reset()); // -> InvalidMarkException
LOG.info("byteBuffer0.rewind()....: {}", byteBuffer.rewind());
LOG.info("byteBuffer0.slice().....: {}", byteBuffer.slice());
byteBuffer.rewind();
byteBuffer.position(position);
byteBuffer.limit (limit);
LOG.info("byteBuffer..............: {}", byteBuffer);
}
final BodyPublisher pub = new ByteArrayBodyPublisherIterator(byteBuffers);
LOG_ENABLED.set(false); // Enable Logging now we've got things set up.
final HttpRequest request = HttpRequest.newBuilder()
.uri(new URI("http://localhost:631"))
.headers("Content-Type", "application/ipp")
.POST(pub)
.build();
HttpResponse<byte[]> response = HttpClient
.newBuilder()
.build()
.send(request, HttpResponse.BodyHandlers.ofByteArray());
LOG.info("Result......: {} {}", response, response.body());
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句