package com.apollographql.apollo; import com.apollographql.apollo.cache.http.HttpCacheRecord; import com.apollographql.apollo.cache.http.HttpCacheRecordEditor; import com.apollographql.apollo.cache.http.HttpCacheStore; import java.io.File; import java.io.IOException; import javax.annotation.Nonnull; import okhttp3.internal.cache.DiskLruCache; import okhttp3.internal.io.FileSystem; import okio.Buffer; import okio.Sink; import okio.Source; import okio.Timeout; class FaultyHttpCacheStore implements HttpCacheStore { private static final int VERSION = 99991; private static final int ENTRY_HEADERS = 0; private static final int ENTRY_BODY = 1; private static final int ENTRY_COUNT = 2; private DiskLruCache cache; private final FaultySource faultySource = new FaultySource(); private final FaultySink faultySink = new FaultySink(); private FailStrategy failStrategy; FaultyHttpCacheStore(FileSystem fileSystem) { this.cache = DiskLruCache.create(fileSystem, new File("/cache/"), VERSION, ENTRY_COUNT, Integer.MAX_VALUE); } @Override public HttpCacheRecord cacheRecord(@Nonnull String cacheKey) throws IOException { final DiskLruCache.Snapshot snapshot = cache.get(cacheKey); if (snapshot == null) { return null; } return new HttpCacheRecord() { @Nonnull @Override public Source headerSource() { if (failStrategy == FailStrategy.FAIL_HEADER_READ) { return faultySource; } else { return snapshot.getSource(ENTRY_HEADERS); } } @Nonnull @Override public Source bodySource() { if (failStrategy == FailStrategy.FAIL_BODY_READ) { return faultySource; } else { return snapshot.getSource(ENTRY_BODY); } } @Override public void close() { snapshot.close(); } }; } @Override public HttpCacheRecordEditor cacheRecordEditor(@Nonnull String cacheKey) throws IOException { final DiskLruCache.Editor editor = cache.edit(cacheKey); if (editor == null) { return null; } return new HttpCacheRecordEditor() { @Nonnull @Override public Sink headerSink() { if (failStrategy == FailStrategy.FAIL_HEADER_WRITE) { return faultySink; } else { return editor.newSink(ENTRY_HEADERS); } } @Nonnull @Override public Sink bodySink() { if (failStrategy == FailStrategy.FAIL_BODY_WRITE) { return faultySink; } else { return editor.newSink(ENTRY_BODY); } } @Override public void abort() throws IOException { editor.abort(); } @Override public void commit() throws IOException { editor.commit(); } }; } @Override public void delete() throws IOException { cache.delete(); } @Override public void remove(@Nonnull String cacheKey) throws IOException { cache.remove(cacheKey); } void failStrategy(FailStrategy failStrategy) { this.failStrategy = failStrategy; } enum FailStrategy { FAIL_HEADER_READ, FAIL_BODY_READ, FAIL_HEADER_WRITE, FAIL_BODY_WRITE } private static class FaultySource implements Source { @Override public long read(Buffer sink, long byteCount) throws IOException { throw new IOException("failed to read"); } @Override public Timeout timeout() { return new Timeout(); } @Override public void close() throws IOException { } } private static class FaultySink implements Sink { @Override public void write(Buffer source, long byteCount) throws IOException { throw new IOException("failed to write"); } @Override public void flush() throws IOException { } @Override public Timeout timeout() { return new Timeout(); } @Override public void close() throws IOException { } } }