mirror of
https://github.com/square/okhttp.git
synced 2025-11-24 18:41:06 +03:00
Add a strict mode of sorts to InMemoryFileSystem to detect handle leaks.
This commit is contained in:
@@ -18,32 +18,95 @@ package com.squareup.okhttp.internal.io;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import okio.Buffer;
|
||||
import okio.ForwardingSink;
|
||||
import okio.ForwardingSource;
|
||||
import okio.Sink;
|
||||
import okio.Source;
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
/** A simple file system where all files are held in memory. Not safe for concurrent use. */
|
||||
public final class InMemoryFileSystem implements FileSystem {
|
||||
public final class InMemoryFileSystem implements FileSystem, TestRule {
|
||||
private final Map<File, Buffer> files = new LinkedHashMap<>();
|
||||
private final Map<Source, File> openSources = new IdentityHashMap<>();
|
||||
private final Map<Sink, File> openSinks = new IdentityHashMap<>();
|
||||
|
||||
@Override public Statement apply(final Statement base, Description description) {
|
||||
return new Statement() {
|
||||
@Override public void evaluate() throws Throwable {
|
||||
base.evaluate();
|
||||
ensureResourcesClosed();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void ensureResourcesClosed() {
|
||||
List<String> openResources = new ArrayList<>();
|
||||
for (File file : openSources.values()) {
|
||||
openResources.add("Source for " + file);
|
||||
}
|
||||
for (File file : openSinks.values()) {
|
||||
openResources.add("Sink for " + file);
|
||||
}
|
||||
if (!openResources.isEmpty()) {
|
||||
StringBuilder builder = new StringBuilder("Resources acquired but not closed:");
|
||||
for (String resource : openResources) {
|
||||
builder.append("\n * ").append(resource);
|
||||
}
|
||||
throw new IllegalStateException(builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override public Source source(File file) throws FileNotFoundException {
|
||||
Buffer result = files.get(file);
|
||||
if (result == null) throw new FileNotFoundException();
|
||||
return result.clone();
|
||||
|
||||
final Source source = result.clone();
|
||||
openSources.put(source, file);
|
||||
|
||||
return new ForwardingSource(source) {
|
||||
@Override public void close() throws IOException {
|
||||
openSources.remove(source);
|
||||
super.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override public Sink sink(File file) throws FileNotFoundException {
|
||||
Buffer result = new Buffer();
|
||||
files.put(file, result);
|
||||
return result;
|
||||
return sink(file, false);
|
||||
}
|
||||
|
||||
@Override public Sink appendingSink(File file) throws FileNotFoundException {
|
||||
Buffer result = files.get(file);
|
||||
return result != null ? result : sink(file);
|
||||
return sink(file, true);
|
||||
}
|
||||
|
||||
private Sink sink(File file, boolean appending) {
|
||||
Buffer result = null;
|
||||
if (appending) {
|
||||
result = files.get(file);
|
||||
}
|
||||
if (result == null) {
|
||||
result = new Buffer();
|
||||
}
|
||||
files.put(file, result);
|
||||
|
||||
final Sink sink = result;
|
||||
openSinks.put(sink, file);
|
||||
|
||||
return new ForwardingSink(sink) {
|
||||
@Override public void close() throws IOException {
|
||||
openSinks.remove(sink);
|
||||
super.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override public void delete(File file) throws IOException {
|
||||
|
||||
Reference in New Issue
Block a user