FileSystemRecordPersister (#137)
* FileSystemRecordPersister * checkstyle
This commit is contained in:
parent
f353a5ff8e
commit
9395d8a36c
6 changed files with 182 additions and 4 deletions
71
filesystem/src/main/java/com/nytimes/android/external/fs/FileSystemRecordPersister.java
vendored
Normal file
71
filesystem/src/main/java/com/nytimes/android/external/fs/FileSystemRecordPersister.java
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
package com.nytimes.android.external.fs;
|
||||
|
||||
import com.nytimes.android.external.fs.filesystem.FileSystem;
|
||||
import com.nytimes.android.external.store.base.Persister;
|
||||
import com.nytimes.android.external.store.base.RecordProvider;
|
||||
import com.nytimes.android.external.store.base.RecordState;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
import okio.BufferedSource;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* FileSystemRecordPersister is used when persisting to/from file system while being stale aware
|
||||
* PathResolver will be used in creating file system paths based on cache keys.
|
||||
* Make sure to have keys containing same data resolve to same "path"
|
||||
*
|
||||
* @param <Key> key type
|
||||
*/
|
||||
public final class FileSystemRecordPersister<Key> implements Persister<BufferedSource, Key>, RecordProvider<Key> {
|
||||
private final FSReader<Key> fileReader;
|
||||
private final FSWriter<Key> fileWriter;
|
||||
private final FileSystem fileSystem;
|
||||
private final PathResolver<Key> pathResolver;
|
||||
private final long expirationDuration;
|
||||
@Nonnull
|
||||
private final TimeUnit expirationUnit;
|
||||
|
||||
private FileSystemRecordPersister(FileSystem fileSystem, PathResolver<Key> pathResolver,
|
||||
long expirationDuration,
|
||||
@Nonnull TimeUnit expirationUnit) {
|
||||
this.fileSystem = fileSystem;
|
||||
this.pathResolver = pathResolver;
|
||||
this.expirationDuration = expirationDuration;
|
||||
this.expirationUnit = expirationUnit;
|
||||
fileReader = new FSReader<>(fileSystem, pathResolver);
|
||||
fileWriter = new FSWriter<>(fileSystem, pathResolver);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <T> FileSystemRecordPersister<T> create(FileSystem fileSystem,
|
||||
PathResolver<T> pathResolver,
|
||||
long expirationDuration,
|
||||
@Nonnull TimeUnit expirationUnit) {
|
||||
if (fileSystem == null) {
|
||||
throw new IllegalArgumentException("root file cannot be null.");
|
||||
}
|
||||
return new FileSystemRecordPersister<>(fileSystem, pathResolver,
|
||||
expirationDuration, expirationUnit);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public RecordState getRecordState(@Nonnull Key key) {
|
||||
return fileSystem.getRecordState(expirationUnit, expirationDuration, pathResolver.resolve(key));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Observable<BufferedSource> read(@Nonnull Key key) {
|
||||
return fileReader.read(key);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Observable<Boolean> write(@Nonnull Key key, @Nonnull BufferedSource bufferedSource) {
|
||||
return fileWriter.write(key, bufferedSource);
|
||||
}
|
||||
}
|
107
filesystem/src/test/java/com/nytimes/android/external/fs/FileSystemRecordPersisterTest.java
vendored
Normal file
107
filesystem/src/test/java/com/nytimes/android/external/fs/FileSystemRecordPersisterTest.java
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
package com.nytimes.android.external.fs;
|
||||
|
||||
import com.nytimes.android.external.fs.filesystem.FileSystem;
|
||||
import com.nytimes.android.external.store.base.RecordState;
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okio.BufferedSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class FileSystemRecordPersisterTest {
|
||||
@Rule
|
||||
public ExpectedException expectedException = ExpectedException.none();
|
||||
|
||||
@Mock
|
||||
FileSystem fileSystem;
|
||||
@Mock
|
||||
BufferedSource bufferedSource;
|
||||
|
||||
private final BarCode simple = new BarCode("type", "key");
|
||||
private final String resolvedPath = new BarCodePathResolver().resolve(simple);
|
||||
private FileSystemRecordPersister<BarCode> fileSystemPersister;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
fileSystemPersister = FileSystemRecordPersister.create(fileSystem,
|
||||
new BarCodePathResolver(),
|
||||
1, TimeUnit.DAYS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readExists() throws FileNotFoundException {
|
||||
when(fileSystem.exists(resolvedPath))
|
||||
.thenReturn(true);
|
||||
when(fileSystem.read(resolvedPath)).thenReturn(bufferedSource);
|
||||
|
||||
BufferedSource returnedValue = fileSystemPersister.read(simple).toBlocking().single();
|
||||
assertThat(returnedValue).isEqualTo(bufferedSource);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readDoesNotExist() throws FileNotFoundException {
|
||||
expectedException.expect(NoSuchElementException.class);
|
||||
when(fileSystem.exists(resolvedPath))
|
||||
.thenReturn(false);
|
||||
|
||||
fileSystemPersister.read(simple).toBlocking().single();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeThenRead() throws IOException {
|
||||
when(fileSystem.read(resolvedPath)).thenReturn(bufferedSource);
|
||||
when(fileSystem.exists(resolvedPath)).thenReturn(true);
|
||||
fileSystemPersister.write(simple, bufferedSource).toBlocking().single();
|
||||
BufferedSource source = fileSystemPersister.read(simple).toBlocking().first();
|
||||
InOrder inOrder = inOrder(fileSystem);
|
||||
inOrder.verify(fileSystem).write(resolvedPath, bufferedSource);
|
||||
inOrder.verify(fileSystem).exists(resolvedPath);
|
||||
inOrder.verify(fileSystem).read(resolvedPath);
|
||||
|
||||
assertThat(source).isEqualTo(bufferedSource);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void freshTest() {
|
||||
when(fileSystem.getRecordState(TimeUnit.DAYS, 1L, resolvedPath))
|
||||
.thenReturn(RecordState.FRESH);
|
||||
|
||||
assertThat(fileSystemPersister.getRecordState(simple)).isEqualTo(RecordState.FRESH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void staleTest() {
|
||||
when(fileSystem.getRecordState(TimeUnit.DAYS, 1L, resolvedPath))
|
||||
.thenReturn(RecordState.STALE);
|
||||
|
||||
assertThat(fileSystemPersister.getRecordState(simple)).isEqualTo(RecordState.STALE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingTest() {
|
||||
when(fileSystem.getRecordState(TimeUnit.DAYS, 1L, resolvedPath))
|
||||
.thenReturn(RecordState.MISSING);
|
||||
|
||||
assertThat(fileSystemPersister.getRecordState(simple)).isEqualTo(RecordState.MISSING);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -3,8 +3,8 @@ package com.nytimes.android.external.fs;
|
|||
|
||||
import com.google.gson.Gson;
|
||||
import com.nytimes.android.external.store.base.Fetcher;
|
||||
import com.nytimes.android.external.store.base.impl.Store;
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
import com.nytimes.android.external.store.base.impl.Store;
|
||||
import com.nytimes.android.external.store.base.impl.StoreBuilder;
|
||||
import com.nytimes.android.external.store.middleware.GsonSourceParser;
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ package com.nytimes.android.external.fs;
|
|||
|
||||
import com.google.gson.Gson;
|
||||
import com.nytimes.android.external.store.base.Fetcher;
|
||||
import com.nytimes.android.external.store.base.impl.Store;
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
import com.nytimes.android.external.store.base.impl.Store;
|
||||
import com.nytimes.android.external.store.base.impl.StoreBuilder;
|
||||
import com.nytimes.android.external.store.middleware.GsonSourceParser;
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ package com.nytimes.android.external.fs;
|
|||
|
||||
import com.nytimes.android.external.store.base.Fetcher;
|
||||
import com.nytimes.android.external.store.base.RecordState;
|
||||
import com.nytimes.android.external.store.base.impl.Store;
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
import com.nytimes.android.external.store.base.impl.Store;
|
||||
import com.nytimes.android.external.store.base.impl.StoreBuilder;
|
||||
|
||||
import org.junit.Before;
|
||||
|
|
|
@ -2,8 +2,8 @@ package com.nytimes.android.external.fs;
|
|||
|
||||
import com.nytimes.android.external.store.base.Fetcher;
|
||||
import com.nytimes.android.external.store.base.RecordState;
|
||||
import com.nytimes.android.external.store.base.impl.Store;
|
||||
import com.nytimes.android.external.store.base.impl.BarCode;
|
||||
import com.nytimes.android.external.store.base.impl.Store;
|
||||
import com.nytimes.android.external.store.base.impl.StoreBuilder;
|
||||
|
||||
import org.junit.Before;
|
||||
|
|
Loading…
Reference in a new issue