Commit 49eb27da by falhassen Committed by Oliver Woodman

Add a CacheKeyFactory to CacheDataSource.

A CacheKeyFactory can be passed to the CacheDataSource constructor, allowing clients to dynamically generate a custom cache key for any given upstream uri.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=198878723
parent 536bd682
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
### dev-v2 (not yet released) ### ### dev-v2 (not yet released) ###
* Allow apps to pass a `CacheKeyFactory` for setting custom cache keys when
creating a `CacheDataSource`.
* Turned on Java 8 compiler support for the ExoPlayer library. Apps that depend * Turned on Java 8 compiler support for the ExoPlayer library. Apps that depend
on ExoPlayer via its source code rather than an AAR may need to add on ExoPlayer via its source code rather than an AAR may need to add
`compileOptions { targetCompatibility JavaVersion.VERSION_1_8 }` to their `compileOptions { targetCompatibility JavaVersion.VERSION_1_8 }` to their
......
...@@ -119,6 +119,7 @@ public final class CacheDataSource implements DataSource { ...@@ -119,6 +119,7 @@ public final class CacheDataSource implements DataSource {
private final DataSource cacheReadDataSource; private final DataSource cacheReadDataSource;
private final DataSource cacheWriteDataSource; private final DataSource cacheWriteDataSource;
private final DataSource upstreamDataSource; private final DataSource upstreamDataSource;
private final CacheKeyFactory cacheKeyFactory;
@Nullable private final EventListener eventListener; @Nullable private final EventListener eventListener;
private final boolean blockOnCache; private final boolean blockOnCache;
...@@ -178,8 +179,13 @@ public final class CacheDataSource implements DataSource { ...@@ -178,8 +179,13 @@ public final class CacheDataSource implements DataSource {
*/ */
public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags, public CacheDataSource(Cache cache, DataSource upstream, @Flags int flags,
long maxCacheFileSize) { long maxCacheFileSize) {
this(cache, upstream, new FileDataSource(), new CacheDataSink(cache, maxCacheFileSize), this(
flags, null); cache,
upstream,
new FileDataSource(),
new CacheDataSink(cache, maxCacheFileSize),
flags,
/* eventListener= */ null);
} }
/** /**
...@@ -198,8 +204,43 @@ public final class CacheDataSource implements DataSource { ...@@ -198,8 +204,43 @@ public final class CacheDataSource implements DataSource {
*/ */
public CacheDataSource(Cache cache, DataSource upstream, DataSource cacheReadDataSource, public CacheDataSource(Cache cache, DataSource upstream, DataSource cacheReadDataSource,
DataSink cacheWriteDataSink, @Flags int flags, @Nullable EventListener eventListener) { DataSink cacheWriteDataSink, @Flags int flags, @Nullable EventListener eventListener) {
this(
cache,
upstream,
cacheReadDataSource,
cacheWriteDataSink,
flags,
eventListener,
/* cacheKeyFactory= */ null);
}
/**
* Constructs an instance with arbitrary {@link DataSource} and {@link DataSink} instances for
* reading and writing the cache. One use of this constructor is to allow data to be transformed
* before it is written to disk.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in the cache.
* @param cacheReadDataSource A {@link DataSource} for reading data from the cache.
* @param cacheWriteDataSink A {@link DataSink} for writing data to the cache. If null, cache is
* accessed read-only.
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR}
* and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0.
* @param eventListener An optional {@link EventListener} to receive events.
* @param cacheKeyFactory An optional factory for cache keys.
*/
public CacheDataSource(
Cache cache,
DataSource upstream,
DataSource cacheReadDataSource,
DataSink cacheWriteDataSink,
@Flags int flags,
@Nullable EventListener eventListener,
@Nullable CacheKeyFactory cacheKeyFactory) {
this.cache = cache; this.cache = cache;
this.cacheReadDataSource = cacheReadDataSource; this.cacheReadDataSource = cacheReadDataSource;
this.cacheKeyFactory =
cacheKeyFactory != null ? cacheKeyFactory : CacheUtil.DEFAULT_CACHE_KEY_FACTORY;
this.blockOnCache = (flags & FLAG_BLOCK_ON_CACHE) != 0; this.blockOnCache = (flags & FLAG_BLOCK_ON_CACHE) != 0;
this.ignoreCacheOnError = (flags & FLAG_IGNORE_CACHE_ON_ERROR) != 0; this.ignoreCacheOnError = (flags & FLAG_IGNORE_CACHE_ON_ERROR) != 0;
this.ignoreCacheForUnsetLengthRequests = this.ignoreCacheForUnsetLengthRequests =
...@@ -216,7 +257,7 @@ public final class CacheDataSource implements DataSource { ...@@ -216,7 +257,7 @@ public final class CacheDataSource implements DataSource {
@Override @Override
public long open(DataSpec dataSpec) throws IOException { public long open(DataSpec dataSpec) throws IOException {
try { try {
key = CacheUtil.getKey(dataSpec); key = cacheKeyFactory.buildCacheKey(dataSpec);
uri = dataSpec.uri; uri = dataSpec.uri;
actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri); actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri);
flags = dataSpec.flags; flags = dataSpec.flags;
......
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.upstream.cache;
import android.net.Uri;
import com.google.android.exoplayer2.upstream.DataSpec;
/** Factory for custom cache keys. */
public interface CacheKeyFactory {
/**
* Returns a custom cache key for the given {@link Uri}.
*
* @param dataSpec The upstream source from which data will be read and cached.
*/
String buildCacheKey(DataSpec dataSpec);
}
...@@ -54,6 +54,15 @@ public final class CacheUtil { ...@@ -54,6 +54,15 @@ public final class CacheUtil {
/** Default buffer size to be used while caching. */ /** Default buffer size to be used while caching. */
public static final int DEFAULT_BUFFER_SIZE_BYTES = 128 * 1024; public static final int DEFAULT_BUFFER_SIZE_BYTES = 128 * 1024;
/** Default {@link CacheKeyFactory} that calls through to {@link #getKey}. */
public static final CacheKeyFactory DEFAULT_CACHE_KEY_FACTORY =
new CacheKeyFactory() {
@Override
public String buildCacheKey(DataSpec dataSpec) {
return getKey(dataSpec);
}
};
/** /**
* Generates a cache key out of the given {@link Uri}. * Generates a cache key out of the given {@link Uri}.
* *
......
...@@ -46,16 +46,27 @@ public final class CacheDataSourceTest { ...@@ -46,16 +46,27 @@ public final class CacheDataSourceTest {
private static final byte[] TEST_DATA = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; private static final byte[] TEST_DATA = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
private static final int MAX_CACHE_FILE_SIZE = 3; private static final int MAX_CACHE_FILE_SIZE = 3;
private static final String CACHE_KEY_PREFIX = "myCacheKeyFactoryPrefix";
private Uri testDataUri; private Uri testDataUri;
private String testDataKey; private String fixedCacheKey;
private String expectedCacheKey;
private File tempFolder; private File tempFolder;
private SimpleCache cache; private SimpleCache cache;
private CacheKeyFactory cacheKeyFactory;
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
testDataUri = Uri.parse("test_data"); testDataUri = Uri.parse("test_data");
testDataKey = CacheUtil.generateKey(testDataUri); fixedCacheKey = CacheUtil.generateKey(testDataUri);
expectedCacheKey = fixedCacheKey;
cacheKeyFactory =
new CacheKeyFactory() {
@Override
public String buildCacheKey(DataSpec dataSpec) {
return CACHE_KEY_PREFIX + "." + CacheUtil.generateKey(dataSpec.uri);
}
};
tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest"); tempFolder = Util.createTempDirectory(RuntimeEnvironment.application, "ExoPlayerTest");
cache = new SimpleCache(tempFolder, new NoOpCacheEvictor()); cache = new SimpleCache(tempFolder, new NoOpCacheEvictor());
} }
...@@ -78,40 +89,202 @@ public final class CacheDataSourceTest { ...@@ -78,40 +89,202 @@ public final class CacheDataSourceTest {
} }
@Test @Test
public void testCacheAndRead() throws Exception {
assertCacheAndRead(false, false);
}
@Test
public void testCacheAndReadUnboundedRequest() throws Exception { public void testCacheAndReadUnboundedRequest() throws Exception {
assertCacheAndRead(true, false); assertCacheAndRead(/* unboundedRequest= */ true, /* simulateUnknownLength= */ false);
} }
@Test @Test
public void testCacheAndReadUnknownLength() throws Exception { public void testCacheAndReadUnknownLength() throws Exception {
assertCacheAndRead(false, true); assertCacheAndRead(/* unboundedRequest= */ false, /* simulateUnknownLength= */ true);
} }
@Test @Test
public void testCacheAndReadUnboundedRequestUnknownLength() throws Exception { public void testCacheAndReadUnboundedRequestUnknownLength() throws Exception {
assertCacheAndRead(true, true); assertCacheAndRead(/* unboundedRequest= */ true, /* simulateUnknownLength= */ true);
}
@Test
public void testCacheAndRead() throws Exception {
assertCacheAndRead(/* unboundedRequest= */ false, /* simulateUnknownLength= */ false);
} }
@Test @Test
public void testUnsatisfiableRange() throws Exception { public void testUnsatisfiableRange() throws Exception {
// Bounded request but the content length is unknown. This forces all data to be cached but not // Bounded request but the content length is unknown. This forces all data to be cached but not
// the length // the length
assertCacheAndRead(false, true); assertCacheAndRead(/* unboundedRequest= */ false, /* simulateUnknownLength= */ true);
// Now do an unbounded request. This will read all of the data from cache and then try to read // Now do an unbounded request. This will read all of the data from cache and then try to read
// more from upstream which will cause to a 416 so CDS will store the length. // more from upstream which will cause to a 416 so CDS will store the length.
CacheDataSource cacheDataSource = createCacheDataSource(true, true); CacheDataSource cacheDataSource =
assertReadDataContentLength(cacheDataSource, true, true); createCacheDataSource(/* setReadException= */ true, /* simulateUnknownLength= */ true);
assertReadDataContentLength(
cacheDataSource, /* unboundedRequest= */ true, /* unknownLength= */ true);
// If the user try to access off range then it should throw an IOException // If the user try to access off range then it should throw an IOException
try { try {
cacheDataSource = createCacheDataSource(false, false); cacheDataSource =
cacheDataSource.open(new DataSpec(testDataUri, TEST_DATA.length, 5, testDataKey)); createCacheDataSource(/* setReadException= */ false, /* simulateUnknownLength= */ false);
cacheDataSource.open(new DataSpec(testDataUri, TEST_DATA.length, 5, fixedCacheKey));
fail();
} catch (IOException e) {
// success
}
}
@Test
public void testCacheAndReadUnboundedRequestWithCacheKeyFactoryWithNullDataSpecCacheKey()
throws Exception {
fixedCacheKey = null;
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, /* key= */ null));
assertCacheAndRead(
/* unboundedRequest= */ true, /* simulateUnknownLength= */ false, cacheKeyFactory);
}
@Test
public void testCacheAndReadUnknownLengthWithCacheKeyFactoryOverridingWithNullDataSpecCacheKey()
throws Exception {
fixedCacheKey = null;
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, /* key= */ null));
assertCacheAndRead(
/* unboundedRequest= */ false, /* simulateUnknownLength= */ true, cacheKeyFactory);
}
@Test
public void
testCacheAndReadUnboundedRequestUnknownLengthWithCacheKeyFactoryWithNullDataSpecCacheKey()
throws Exception {
fixedCacheKey = null;
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, /* key= */ null));
assertCacheAndRead(
/* unboundedRequest= */ true, /* simulateUnknownLength= */ true, cacheKeyFactory);
}
@Test
public void testCacheAndReadWithCacheKeyFactoryWithNullDataSpecCacheKey() throws Exception {
fixedCacheKey = null;
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, /* key= */ null));
assertCacheAndRead(
/* unboundedRequest= */ false, /* simulateUnknownLength= */ false, cacheKeyFactory);
}
@Test
public void testUnsatisfiableRangeWithCacheKeyFactoryNullDataSpecCacheKey() throws Exception {
fixedCacheKey = null;
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, /* key= */ null));
// Bounded request but the content length is unknown. This forces all data to be cached but not
// the length
assertCacheAndRead(
/* unboundedRequest= */ false, /* simulateUnknownLength= */ true, cacheKeyFactory);
// Now do an unbounded request. This will read all of the data from cache and then try to read
// more from upstream which will cause to a 416 so CDS will store the length.
CacheDataSource cacheDataSource =
createCacheDataSource(
/* setReadException= */ true, /* simulateUnknownLength= */ true, cacheKeyFactory);
assertReadDataContentLength(
cacheDataSource, /* unboundedRequest= */ true, /* unknownLength= */ true);
// If the user try to access off range then it should throw an IOException
try {
cacheDataSource =
createCacheDataSource(
/* setReadException= */ false, /* simulateUnknownLength= */ false, cacheKeyFactory);
cacheDataSource.open(new DataSpec(testDataUri, TEST_DATA.length, 5, fixedCacheKey));
fail();
} catch (IOException e) {
// success
}
}
@Test
public void testCacheAndReadUnboundedRequestWithCacheKeyFactoryOverridingDataSpecCacheKey()
throws Exception {
fixedCacheKey = CacheUtil.generateKey(testDataUri);
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, fixedCacheKey));
assertCacheAndRead(true, false, cacheKeyFactory);
}
@Test
public void testCacheAndReadUnknownLengthWithCacheKeyFactoryOverridingDataSpecCacheKey()
throws Exception {
fixedCacheKey = CacheUtil.generateKey(testDataUri);
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, fixedCacheKey));
assertCacheAndRead(false, true, cacheKeyFactory);
}
@Test
public void
testCacheAndReadUnboundedRequestUnknownLengthWithCacheKeyFactoryOverridingDataSpecCacheKey()
throws Exception {
fixedCacheKey = CacheUtil.generateKey(testDataUri);
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, fixedCacheKey));
assertCacheAndRead(
/* unboundedRequest= */ true, /* simulateUnknownLength= */ true, cacheKeyFactory);
}
@Test
public void testCacheAndReadWithCacheKeyFactoryOverridingDataSpecCacheKey() throws Exception {
fixedCacheKey = CacheUtil.generateKey(testDataUri);
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, fixedCacheKey));
assertCacheAndRead(
/* unboundedRequest= */ false, /* simulateUnknownLength= */ false, cacheKeyFactory);
}
@Test
public void testUnsatisfiableRangeWithCacheKeyFactoryOverridingDataSpecCacheKey()
throws Exception {
fixedCacheKey = CacheUtil.generateKey(testDataUri);
expectedCacheKey =
cacheKeyFactory.buildCacheKey(
new DataSpec(testDataUri, TEST_DATA.length, 5, fixedCacheKey));
// Bounded request but the content length is unknown. This forces all data to be cached but not
// the length
assertCacheAndRead(
/* unboundedRequest= */ false, /* simulateUnknownLength= */ true, cacheKeyFactory);
// Now do an unbounded request. This will read all of the data from cache and then try to read
// more from upstream which will cause to a 416 so CDS will store the length.
CacheDataSource cacheDataSource =
createCacheDataSource(
/* setReadException= */ true, /* simulateUnknownLength= */ true, cacheKeyFactory);
assertReadDataContentLength(
cacheDataSource, /* unboundedRequest= */ true, /* unknownLength= */ true);
// If the user try to access off range then it should throw an IOException
try {
cacheDataSource =
createCacheDataSource(
/* setReadException= */ false, /* simulateUnknownLength= */ false, cacheKeyFactory);
cacheDataSource.open(new DataSpec(testDataUri, TEST_DATA.length, 5, fixedCacheKey));
fail(); fail();
} catch (IOException e) { } catch (IOException e) {
// success // success
...@@ -123,7 +296,7 @@ public final class CacheDataSourceTest { ...@@ -123,7 +296,7 @@ public final class CacheDataSourceTest {
// Read partial at EOS but don't cross it so length is unknown // Read partial at EOS but don't cross it so length is unknown
CacheDataSource cacheDataSource = createCacheDataSource(false, true); CacheDataSource cacheDataSource = createCacheDataSource(false, true);
assertReadData(cacheDataSource, true, TEST_DATA.length - 2, 2); assertReadData(cacheDataSource, true, TEST_DATA.length - 2, 2);
assertThat(cache.getContentLength(testDataKey)).isEqualTo(C.LENGTH_UNSET); assertThat(cache.getContentLength(expectedCacheKey)).isEqualTo(C.LENGTH_UNSET);
// Now do an unbounded request for whole data. This will cause a bounded request from upstream. // Now do an unbounded request for whole data. This will cause a bounded request from upstream.
// End of data from upstream shouldn't be mixed up with EOS and cause length set wrong. // End of data from upstream shouldn't be mixed up with EOS and cause length set wrong.
...@@ -133,7 +306,7 @@ public final class CacheDataSourceTest { ...@@ -133,7 +306,7 @@ public final class CacheDataSourceTest {
// Now the length set correctly do an unbounded request with offset // Now the length set correctly do an unbounded request with offset
assertThat( assertThat(
cacheDataSource.open( cacheDataSource.open(
new DataSpec(testDataUri, TEST_DATA.length - 2, C.LENGTH_UNSET, testDataKey))) new DataSpec(testDataUri, TEST_DATA.length - 2, C.LENGTH_UNSET, expectedCacheKey)))
.isEqualTo(2); .isEqualTo(2);
// An unbounded request with offset for not cached content // An unbounded request with offset for not cached content
...@@ -155,12 +328,12 @@ public final class CacheDataSourceTest { ...@@ -155,12 +328,12 @@ public final class CacheDataSourceTest {
CacheDataSource cacheDataSource = new CacheDataSource(cache, upstream, 0); CacheDataSource cacheDataSource = new CacheDataSource(cache, upstream, 0);
int flags = DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH; int flags = DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH;
cacheDataSource.open(new DataSpec(testDataUri, 0, C.LENGTH_UNSET, testDataKey, flags)); cacheDataSource.open(new DataSpec(testDataUri, 0, C.LENGTH_UNSET, expectedCacheKey, flags));
TestUtil.readToEnd(cacheDataSource); TestUtil.readToEnd(cacheDataSource);
cacheDataSource.close(); cacheDataSource.close();
assertThat(upstream.getAndClearOpenedDataSpecs()).hasLength(1); assertThat(upstream.getAndClearOpenedDataSpecs()).hasLength(1);
assertThat(cache.getContentLength(testDataKey)).isEqualTo(TEST_DATA.length); assertThat(cache.getContentLength(expectedCacheKey)).isEqualTo(TEST_DATA.length);
} }
@Test @Test
...@@ -171,7 +344,7 @@ public final class CacheDataSourceTest { ...@@ -171,7 +344,7 @@ public final class CacheDataSourceTest {
new CacheDataSource( new CacheDataSource(
cache, upstream, CacheDataSource.FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS); cache, upstream, CacheDataSource.FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS);
cacheDataSource.open(new DataSpec(testDataUri, 0, C.LENGTH_UNSET, testDataKey)); cacheDataSource.open(new DataSpec(testDataUri, 0, C.LENGTH_UNSET, expectedCacheKey));
TestUtil.readToEnd(cacheDataSource); TestUtil.readToEnd(cacheDataSource);
cacheDataSource.close(); cacheDataSource.close();
...@@ -206,7 +379,7 @@ public final class CacheDataSourceTest { ...@@ -206,7 +379,7 @@ public final class CacheDataSourceTest {
new CacheDataSource(cache, upstream, new FileDataSource(), null, 0, null); new CacheDataSource(cache, upstream, new FileDataSource(), null, 0, null);
// Open source and read some data from upstream as the data hasn't cached yet. // Open source and read some data from upstream as the data hasn't cached yet.
DataSpec dataSpec = new DataSpec(testDataUri, 0, C.LENGTH_UNSET, testDataKey); DataSpec dataSpec = new DataSpec(testDataUri, 0, C.LENGTH_UNSET, fixedCacheKey);
cacheDataSource.open(dataSpec); cacheDataSource.open(dataSpec);
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
cacheDataSource.read(buffer, 0, buffer.length); cacheDataSource.read(buffer, 0, buffer.length);
...@@ -245,7 +418,7 @@ public final class CacheDataSourceTest { ...@@ -245,7 +418,7 @@ public final class CacheDataSourceTest {
.appendReadData(1); .appendReadData(1);
// Lock the content on the cache. // Lock the content on the cache.
SimpleCacheSpan cacheSpan = cache.startReadWriteNonBlocking(testDataKey, 0); SimpleCacheSpan cacheSpan = cache.startReadWriteNonBlocking(expectedCacheKey, 0);
assertThat(cacheSpan).isNotNull(); assertThat(cacheSpan).isNotNull();
assertThat(cacheSpan.isHoleSpan()).isTrue(); assertThat(cacheSpan.isHoleSpan()).isTrue();
...@@ -253,7 +426,7 @@ public final class CacheDataSourceTest { ...@@ -253,7 +426,7 @@ public final class CacheDataSourceTest {
CacheDataSource cacheDataSource = new CacheDataSource(cache, upstream, 0); CacheDataSource cacheDataSource = new CacheDataSource(cache, upstream, 0);
// Open source and read some data from upstream without writing to cache as the data is locked. // Open source and read some data from upstream without writing to cache as the data is locked.
DataSpec dataSpec = new DataSpec(testDataUri, 0, C.LENGTH_UNSET, testDataKey); DataSpec dataSpec = new DataSpec(testDataUri, 0, C.LENGTH_UNSET, fixedCacheKey);
cacheDataSource.open(dataSpec); cacheDataSource.open(dataSpec);
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
cacheDataSource.read(buffer, 0, buffer.length); cacheDataSource.read(buffer, 0, buffer.length);
...@@ -286,7 +459,7 @@ public final class CacheDataSourceTest { ...@@ -286,7 +459,7 @@ public final class CacheDataSourceTest {
upstream.getDataSet().newDefaultData().appendReadData(1024).endData(); upstream.getDataSet().newDefaultData().appendReadData(1024).endData();
// Cache the latter half of the data. // Cache the latter half of the data.
DataSpec dataSpec = new DataSpec(testDataUri, 512, C.LENGTH_UNSET, testDataKey); DataSpec dataSpec = new DataSpec(testDataUri, 512, C.LENGTH_UNSET, fixedCacheKey);
CacheUtil.cache(dataSpec, cache, upstream, /* counters= */ null, /* isCanceled= */ null); CacheUtil.cache(dataSpec, cache, upstream, /* counters= */ null, /* isCanceled= */ null);
// Create cache read-only CacheDataSource. // Create cache read-only CacheDataSource.
...@@ -294,12 +467,12 @@ public final class CacheDataSourceTest { ...@@ -294,12 +467,12 @@ public final class CacheDataSourceTest {
new CacheDataSource(cache, upstream, new FileDataSource(), null, 0, null); new CacheDataSource(cache, upstream, new FileDataSource(), null, 0, null);
// Open source and read some data from upstream as the data hasn't cached yet. // Open source and read some data from upstream as the data hasn't cached yet.
dataSpec = new DataSpec(testDataUri, 0, C.LENGTH_UNSET, testDataKey); dataSpec = new DataSpec(testDataUri, 0, C.LENGTH_UNSET, fixedCacheKey);
cacheDataSource.open(dataSpec); cacheDataSource.open(dataSpec);
TestUtil.readExactly(cacheDataSource, 100); TestUtil.readExactly(cacheDataSource, 100);
// Delete cached data. // Delete cached data.
CacheUtil.remove(cache, testDataKey); CacheUtil.remove(cache, expectedCacheKey);
assertCacheEmpty(cache); assertCacheEmpty(cache);
// Read the rest of the data. // Read the rest of the data.
...@@ -317,21 +490,21 @@ public final class CacheDataSourceTest { ...@@ -317,21 +490,21 @@ public final class CacheDataSourceTest {
// Cache the latter half of the data. // Cache the latter half of the data.
int halfDataLength = 512; int halfDataLength = 512;
DataSpec dataSpec = new DataSpec(testDataUri, halfDataLength, C.LENGTH_UNSET, testDataKey); DataSpec dataSpec = new DataSpec(testDataUri, halfDataLength, C.LENGTH_UNSET, fixedCacheKey);
CacheUtil.cache(dataSpec, cache, upstream, /* counters= */ null, /* isCanceled= */ null); CacheUtil.cache(dataSpec, cache, upstream, /* counters= */ null, /* isCanceled= */ null);
// Create blocking CacheDataSource. // Create blocking CacheDataSource.
CacheDataSource cacheDataSource = CacheDataSource cacheDataSource =
new CacheDataSource(cache, upstream, CacheDataSource.FLAG_BLOCK_ON_CACHE); new CacheDataSource(cache, upstream, CacheDataSource.FLAG_BLOCK_ON_CACHE);
dataSpec = new DataSpec(testDataUri, 0, C.LENGTH_UNSET, testDataKey); dataSpec = new DataSpec(testDataUri, 0, C.LENGTH_UNSET, fixedCacheKey);
cacheDataSource.open(dataSpec); cacheDataSource.open(dataSpec);
// Read the first half from upstream as it hasn't cached yet. // Read the first half from upstream as it hasn't cached yet.
TestUtil.readExactly(cacheDataSource, halfDataLength); TestUtil.readExactly(cacheDataSource, halfDataLength);
// Delete the cached latter half. // Delete the cached latter half.
NavigableSet<CacheSpan> cachedSpans = cache.getCachedSpans(testDataKey); NavigableSet<CacheSpan> cachedSpans = cache.getCachedSpans(expectedCacheKey);
for (CacheSpan cachedSpan : cachedSpans) { for (CacheSpan cachedSpan : cachedSpans) {
if (cachedSpan.position >= halfDataLength) { if (cachedSpan.position >= halfDataLength) {
try { try {
...@@ -355,8 +528,30 @@ public final class CacheDataSourceTest { ...@@ -355,8 +528,30 @@ public final class CacheDataSourceTest {
// Just read from cache // Just read from cache
cacheDataSource = createCacheDataSource(true, simulateUnknownLength); cacheDataSource = createCacheDataSource(true, simulateUnknownLength);
assertReadDataContentLength(cacheDataSource, unboundedRequest, assertReadDataContentLength(
false /*length is already cached*/); cacheDataSource,
unboundedRequest,
// Length is already cached.
/* unknownLength= */ false);
}
private void assertCacheAndRead(
boolean unboundedRequest, boolean simulateUnknownLength, CacheKeyFactory cacheKeyFactory)
throws IOException {
// Read all data from upstream and write to cache
CacheDataSource cacheDataSource =
createCacheDataSource(
/* setReadException= */ false, simulateUnknownLength, cacheKeyFactory);
assertReadDataContentLength(cacheDataSource, unboundedRequest, simulateUnknownLength);
// Just read from cache
cacheDataSource =
createCacheDataSource(/* setReadException= */ true, simulateUnknownLength, cacheKeyFactory);
assertReadDataContentLength(
cacheDataSource,
unboundedRequest,
// Length is already cached.
/* unknownLength= */ false);
} }
/** /**
...@@ -368,7 +563,7 @@ public final class CacheDataSourceTest { ...@@ -368,7 +563,7 @@ public final class CacheDataSourceTest {
int length = unboundedRequest ? C.LENGTH_UNSET : TEST_DATA.length; int length = unboundedRequest ? C.LENGTH_UNSET : TEST_DATA.length;
assertReadData(cacheDataSource, unknownLength, 0, length); assertReadData(cacheDataSource, unknownLength, 0, length);
// If !unboundedRequest, CacheDataSource doesn't reach EOS so shouldn't cache content length // If !unboundedRequest, CacheDataSource doesn't reach EOS so shouldn't cache content length
assertThat(cache.getContentLength(testDataKey)) assertThat(cache.getContentLength(expectedCacheKey))
.isEqualTo(!unboundedRequest ? C.LENGTH_UNSET : TEST_DATA.length); .isEqualTo(!unboundedRequest ? C.LENGTH_UNSET : TEST_DATA.length);
} }
...@@ -380,7 +575,11 @@ public final class CacheDataSourceTest { ...@@ -380,7 +575,11 @@ public final class CacheDataSourceTest {
} }
DataSpec dataSpec = DataSpec dataSpec =
new DataSpec( new DataSpec(
testDataUri, position, length, testDataKey, DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH); testDataUri,
position,
length,
fixedCacheKey,
DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH);
assertThat(cacheDataSource.open(dataSpec)).isEqualTo(unknownLength ? length : testDataLength); assertThat(cacheDataSource.open(dataSpec)).isEqualTo(unknownLength ? length : testDataLength);
cacheDataSource.close(); cacheDataSource.close();
...@@ -395,6 +594,16 @@ public final class CacheDataSourceTest { ...@@ -395,6 +594,16 @@ public final class CacheDataSourceTest {
CacheDataSource.FLAG_BLOCK_ON_CACHE); CacheDataSource.FLAG_BLOCK_ON_CACHE);
} }
private CacheDataSource createCacheDataSource(
boolean setReadException, boolean simulateUnknownLength, CacheKeyFactory cacheKeyFactory) {
return createCacheDataSource(
setReadException,
simulateUnknownLength,
CacheDataSource.FLAG_BLOCK_ON_CACHE,
new CacheDataSink(cache, MAX_CACHE_FILE_SIZE),
cacheKeyFactory);
}
private CacheDataSource createCacheDataSource(boolean setReadException, private CacheDataSource createCacheDataSource(boolean setReadException,
boolean simulateUnknownLength, @CacheDataSource.Flags int flags) { boolean simulateUnknownLength, @CacheDataSource.Flags int flags) {
return createCacheDataSource(setReadException, simulateUnknownLength, flags, return createCacheDataSource(setReadException, simulateUnknownLength, flags,
...@@ -404,14 +613,34 @@ public final class CacheDataSourceTest { ...@@ -404,14 +613,34 @@ public final class CacheDataSourceTest {
private CacheDataSource createCacheDataSource(boolean setReadException, private CacheDataSource createCacheDataSource(boolean setReadException,
boolean simulateUnknownLength, @CacheDataSource.Flags int flags, boolean simulateUnknownLength, @CacheDataSource.Flags int flags,
CacheDataSink cacheWriteDataSink) { CacheDataSink cacheWriteDataSink) {
return createCacheDataSource(
setReadException,
simulateUnknownLength,
flags,
cacheWriteDataSink,
/* cacheKeyFactory= */ null);
}
private CacheDataSource createCacheDataSource(
boolean setReadException,
boolean simulateUnknownLength,
@CacheDataSource.Flags int flags,
CacheDataSink cacheWriteDataSink,
CacheKeyFactory cacheKeyFactory) {
FakeDataSource upstream = new FakeDataSource(); FakeDataSource upstream = new FakeDataSource();
FakeData fakeData = upstream.getDataSet().newDefaultData() FakeData fakeData = upstream.getDataSet().newDefaultData()
.setSimulateUnknownLength(simulateUnknownLength).appendReadData(TEST_DATA); .setSimulateUnknownLength(simulateUnknownLength).appendReadData(TEST_DATA);
if (setReadException) { if (setReadException) {
fakeData.appendReadError(new IOException("Shouldn't read from upstream")); fakeData.appendReadError(new IOException("Shouldn't read from upstream"));
} }
return new CacheDataSource(cache, upstream, new FileDataSource(), cacheWriteDataSink, return new CacheDataSource(
flags, null); cache,
upstream,
new FileDataSource(),
cacheWriteDataSink,
flags,
/* eventListener= */ null,
cacheKeyFactory);
} }
} }
...@@ -132,6 +132,22 @@ public final class CacheUtilTest { ...@@ -132,6 +132,22 @@ public final class CacheUtilTest {
} }
@Test @Test
public void testDefaultCacheKeyFactory_buildCacheKey() throws Exception {
Uri testUri = Uri.parse("test");
String key = "key";
// If DataSpec.key is present, returns it
assertThat(
CacheUtil.DEFAULT_CACHE_KEY_FACTORY.buildCacheKey(
new DataSpec(testUri, 0, LENGTH_UNSET, key)))
.isEqualTo(key);
// If not generates a new one using DataSpec.uri
assertThat(
CacheUtil.DEFAULT_CACHE_KEY_FACTORY.buildCacheKey(
new DataSpec(testUri, 0, LENGTH_UNSET, null)))
.isEqualTo(generateKey(testUri));
}
@Test
public void testGetCachedNoData() throws Exception { public void testGetCachedNoData() throws Exception {
CachingCounters counters = new CachingCounters(); CachingCounters counters = new CachingCounters();
CacheUtil.getCached(new DataSpec(Uri.parse("test")), mockCache, counters); CacheUtil.getCached(new DataSpec(Uri.parse("test")), mockCache, counters);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment