mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
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
This commit is contained in:
parent
536bd68294
commit
49eb27da0a
6 changed files with 365 additions and 38 deletions
|
|
@ -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 {
|
||||||
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 {
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
*/
|
*/
|
||||||
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 {
|
||||||
@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;
|
||||||
|
|
|
||||||
30
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheKeyFactory.java
vendored
Normal file
30
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheKeyFactory.java
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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 {
|
||||||
/** 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 {
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
@ -77,41 +88,203 @@ public final class CacheDataSourceTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCacheAndRead() throws Exception {
|
|
||||||
assertCacheAndRead(false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@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 {
|
||||||
// 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 {
|
||||||
// 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
.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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
||||||
// 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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
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 {
|
||||||
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 {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,22 @@ public final class CacheUtilTest {
|
||||||
.isEqualTo(generateKey(testUri));
|
.isEqualTo(generateKey(testUri));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Test
|
||||||
public void testGetCachedNoData() throws Exception {
|
public void testGetCachedNoData() throws Exception {
|
||||||
CachingCounters counters = new CachingCounters();
|
CachingCounters counters = new CachingCounters();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue