Make CachedContent store content length in ContentMetadata
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=188742379
This commit is contained in:
parent
c38e7b1aeb
commit
00a7306fd8
@ -14,9 +14,7 @@ import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests {@link CachedContentIndex}.
|
||||
*/
|
||||
/** Tests {@link CachedContentIndex}. */
|
||||
public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
|
||||
private final byte[] testIndexV1File = {
|
||||
@ -53,14 +51,14 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
final String key3 = "key3";
|
||||
|
||||
// Add two CachedContents with add methods
|
||||
CachedContent cachedContent1 = new CachedContent(5, key1, 10);
|
||||
CachedContent cachedContent1 = new CachedContent(5, key1);
|
||||
index.addNew(cachedContent1);
|
||||
CachedContent cachedContent2 = index.getOrAdd(key2);
|
||||
assertThat(cachedContent1.id != cachedContent2.id).isTrue();
|
||||
|
||||
// add a span
|
||||
File cacheSpanFile = SimpleCacheSpanTest
|
||||
.createCacheSpanFile(cacheDir, cachedContent1.id, 10, 20, 30);
|
||||
File cacheSpanFile =
|
||||
SimpleCacheSpanTest.createCacheSpanFile(cacheDir, cachedContent1.id, 10, 20, 30);
|
||||
SimpleCacheSpan span = SimpleCacheSpan.createCacheEntry(cacheSpanFile, index);
|
||||
assertThat(span).isNotNull();
|
||||
cachedContent1.addSpan(span);
|
||||
@ -115,8 +113,12 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
public void testStoreV1() throws Exception {
|
||||
index.addNew(new CachedContent(2, "KLMNO", 2560));
|
||||
index.addNew(new CachedContent(5, "ABCDE", 10));
|
||||
CachedContent cachedContent1 = new CachedContent(2, "KLMNO");
|
||||
cachedContent1.setLength(2560);
|
||||
index.addNew(cachedContent1);
|
||||
CachedContent cachedContent2 = new CachedContent(5, "ABCDE");
|
||||
cachedContent2.setLength(10);
|
||||
index.addNew(cachedContent2);
|
||||
|
||||
index.store();
|
||||
|
||||
@ -165,8 +167,8 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
byte[] key = "Bar12345Bar12345".getBytes(C.UTF8_NAME); // 128 bit key
|
||||
byte[] key2 = "Foo12345Foo12345".getBytes(C.UTF8_NAME); // 128 bit key
|
||||
|
||||
assertStoredAndLoadedEqual(new CachedContentIndex(cacheDir, key),
|
||||
new CachedContentIndex(cacheDir, key));
|
||||
assertStoredAndLoadedEqual(
|
||||
new CachedContentIndex(cacheDir, key), new CachedContentIndex(cacheDir, key));
|
||||
|
||||
// Rename the index file from the test above
|
||||
File file1 = new File(cacheDir, CachedContentIndex.FILE_NAME);
|
||||
@ -174,8 +176,8 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
assertThat(file1.renameTo(file2)).isTrue();
|
||||
|
||||
// Write a new index file
|
||||
assertStoredAndLoadedEqual(new CachedContentIndex(cacheDir, key),
|
||||
new CachedContentIndex(cacheDir, key));
|
||||
assertStoredAndLoadedEqual(
|
||||
new CachedContentIndex(cacheDir, key), new CachedContentIndex(cacheDir, key));
|
||||
|
||||
assertThat(file1.length()).isEqualTo(file2.length());
|
||||
// Assert file content is different
|
||||
@ -187,8 +189,8 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
|
||||
boolean threw = false;
|
||||
try {
|
||||
assertStoredAndLoadedEqual(new CachedContentIndex(cacheDir, key),
|
||||
new CachedContentIndex(cacheDir, key2));
|
||||
assertStoredAndLoadedEqual(
|
||||
new CachedContentIndex(cacheDir, key), new CachedContentIndex(cacheDir, key2));
|
||||
} catch (AssertionError e) {
|
||||
threw = true;
|
||||
}
|
||||
@ -197,8 +199,8 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
.isTrue();
|
||||
|
||||
try {
|
||||
assertStoredAndLoadedEqual(new CachedContentIndex(cacheDir, key),
|
||||
new CachedContentIndex(cacheDir));
|
||||
assertStoredAndLoadedEqual(
|
||||
new CachedContentIndex(cacheDir, key), new CachedContentIndex(cacheDir));
|
||||
} catch (AssertionError e) {
|
||||
threw = true;
|
||||
}
|
||||
@ -207,18 +209,18 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
.isTrue();
|
||||
|
||||
// Non encrypted index file can be read even when encryption key provided.
|
||||
assertStoredAndLoadedEqual(new CachedContentIndex(cacheDir),
|
||||
new CachedContentIndex(cacheDir, key));
|
||||
assertStoredAndLoadedEqual(
|
||||
new CachedContentIndex(cacheDir), new CachedContentIndex(cacheDir, key));
|
||||
|
||||
// Test multiple store() calls
|
||||
CachedContentIndex index = new CachedContentIndex(cacheDir, key);
|
||||
index.addNew(new CachedContent(15, "key3", 110));
|
||||
index.addNew(new CachedContent(15, "key3"));
|
||||
index.store();
|
||||
assertStoredAndLoadedEqual(index, new CachedContentIndex(cacheDir, key));
|
||||
}
|
||||
|
||||
public void testRemoveEmptyNotLockedCachedContent() throws Exception {
|
||||
CachedContent cachedContent = new CachedContent(5, "key1", 10);
|
||||
CachedContent cachedContent = new CachedContent(5, "key1");
|
||||
index.addNew(cachedContent);
|
||||
|
||||
index.maybeRemove(cachedContent.key);
|
||||
@ -227,7 +229,7 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
public void testCantRemoveNotEmptyCachedContent() throws Exception {
|
||||
CachedContent cachedContent = new CachedContent(5, "key1", 10);
|
||||
CachedContent cachedContent = new CachedContent(5, "key1");
|
||||
index.addNew(cachedContent);
|
||||
File cacheSpanFile =
|
||||
SimpleCacheSpanTest.createCacheSpanFile(cacheDir, cachedContent.id, 10, 20, 30);
|
||||
@ -240,7 +242,7 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
}
|
||||
|
||||
public void testCantRemoveLockedCachedContent() throws Exception {
|
||||
CachedContent cachedContent = new CachedContent(5, "key1", 10);
|
||||
CachedContent cachedContent = new CachedContent(5, "key1");
|
||||
cachedContent.setLocked(true);
|
||||
index.addNew(cachedContent);
|
||||
|
||||
@ -251,7 +253,7 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
|
||||
private void assertStoredAndLoadedEqual(CachedContentIndex index, CachedContentIndex index2)
|
||||
throws IOException {
|
||||
index.addNew(new CachedContent(5, "key1", 10));
|
||||
index.addNew(new CachedContent(5, "key1"));
|
||||
index.getOrAdd("key2");
|
||||
index.store();
|
||||
|
||||
@ -264,5 +266,4 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
||||
assertThat(index2.get(key).getSpans()).isEqualTo(index.get(key).getSpans());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,14 +28,16 @@ import java.util.TreeSet;
|
||||
*/
|
||||
/*package*/ final class CachedContent {
|
||||
|
||||
private static final String EXOPLAYER_METADATA_NAME_PREFIX = "exo_";
|
||||
private static final String METADATA_NAME_LENGTH = EXOPLAYER_METADATA_NAME_PREFIX + "len";
|
||||
/** The cache file id that uniquely identifies the original stream. */
|
||||
public final int id;
|
||||
/** The cache key that uniquely identifies the original stream. */
|
||||
public final String key;
|
||||
/** The cached spans of this content. */
|
||||
private final TreeSet<SimpleCacheSpan> cachedSpans;
|
||||
/** The length of the original stream, or {@link C#LENGTH_UNSET} if the length is unknown. */
|
||||
private long length;
|
||||
/** Metadata values. */
|
||||
private DefaultContentMetadata metadata;
|
||||
/** Whether the content is locked. */
|
||||
private boolean locked;
|
||||
|
||||
@ -45,8 +47,13 @@ import java.util.TreeSet;
|
||||
* @param input Input stream containing values needed to initialize CachedContent instance.
|
||||
* @throws IOException If an error occurs during reading values.
|
||||
*/
|
||||
public CachedContent(DataInputStream input) throws IOException {
|
||||
this(input.readInt(), input.readUTF(), input.readLong());
|
||||
public static CachedContent readFromStream(DataInputStream input) throws IOException {
|
||||
int id = input.readInt();
|
||||
String key = input.readUTF();
|
||||
long length = input.readLong();
|
||||
CachedContent cachedContent = new CachedContent(id, key);
|
||||
cachedContent.setLength(length);
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,12 +61,11 @@ import java.util.TreeSet;
|
||||
*
|
||||
* @param id The cache file id.
|
||||
* @param key The cache stream key.
|
||||
* @param length The length of the original stream.
|
||||
*/
|
||||
public CachedContent(int id, String key, long length) {
|
||||
public CachedContent(int id, String key) {
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
this.length = length;
|
||||
this.metadata = new DefaultContentMetadata();
|
||||
this.cachedSpans = new TreeSet<>();
|
||||
}
|
||||
|
||||
@ -72,17 +78,21 @@ import java.util.TreeSet;
|
||||
public void writeToStream(DataOutputStream output) throws IOException {
|
||||
output.writeInt(id);
|
||||
output.writeUTF(key);
|
||||
output.writeLong(length);
|
||||
output.writeLong(getLength());
|
||||
}
|
||||
|
||||
/** Returns the length of the content. */
|
||||
/**
|
||||
* Returns the length of the original stream, or {@link C#LENGTH_UNSET} if the length is unknown.
|
||||
*/
|
||||
public long getLength() {
|
||||
return length;
|
||||
return metadata.get(METADATA_NAME_LENGTH, C.LENGTH_UNSET);
|
||||
}
|
||||
|
||||
/** Sets the length of the content. */
|
||||
public void setLength(long length) {
|
||||
this.length = length;
|
||||
ContentMetadataMutations mutations =
|
||||
new ContentMetadataMutations().set(METADATA_NAME_LENGTH, length);
|
||||
metadata = new DefaultContentMetadata(metadata, mutations);
|
||||
}
|
||||
|
||||
/** Returns whether the content is locked. */
|
||||
@ -194,6 +204,7 @@ import java.util.TreeSet;
|
||||
|
||||
/** Calculates a hash code for the header of this {@code CachedContent}. */
|
||||
public int headerHashCode() {
|
||||
long length = getLength();
|
||||
int result = id;
|
||||
result = 31 * result + key.hashCode();
|
||||
result = 31 * result + (int) (length ^ (length >>> 32));
|
||||
|
@ -45,9 +45,7 @@ import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* This class maintains the index of cached content.
|
||||
*/
|
||||
/** Maintains the index of cached content. */
|
||||
/*package*/ class CachedContentIndex {
|
||||
|
||||
public static final String FILE_NAME = "cached_content_index.exi";
|
||||
@ -259,7 +257,7 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
int count = input.readInt();
|
||||
int hashCode = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
CachedContent cachedContent = new CachedContent(input);
|
||||
CachedContent cachedContent = CachedContent.readFromStream(input);
|
||||
add(cachedContent);
|
||||
hashCode += cachedContent.headerHashCode();
|
||||
}
|
||||
@ -339,7 +337,8 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
private CachedContent addNew(String key, long length) {
|
||||
int id = getNewId(idToKey);
|
||||
CachedContent cachedContent = new CachedContent(id, key, length);
|
||||
CachedContent cachedContent = new CachedContent(id, key);
|
||||
cachedContent.setLength(length);
|
||||
addNew(cachedContent);
|
||||
return cachedContent;
|
||||
}
|
||||
|
@ -15,63 +15,9 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.upstream.cache;
|
||||
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException;
|
||||
|
||||
/** Interface for accessing cached content metadata which is stored as name, value pairs. */
|
||||
public interface ContentMetadata {
|
||||
|
||||
/**
|
||||
* Interface for modifying values in a {@link ContentMetadata} object. The changes you make in an
|
||||
* editor are not copied back to the original {@link ContentMetadata} until you call {@link
|
||||
* #commit()}.
|
||||
*/
|
||||
interface Editor {
|
||||
/**
|
||||
* Sets a metadata value, to be committed once {@link #commit()} is called. Passing {@code null}
|
||||
* as {@code value} isn't allowed. {@code value} byte array shouldn't be modified after passed
|
||||
* to this method.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
Editor set(String name, byte[] value);
|
||||
/**
|
||||
* Sets a metadata value, to be committed once {@link #commit()} is called. Passing {@code null}
|
||||
* as value isn't allowed.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
Editor set(String name, String value);
|
||||
/**
|
||||
* Sets a metadata value, to be committed once {@link #commit()} is called.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
Editor set(String name, long value);
|
||||
/**
|
||||
* Sets a metadata value, to be committed once {@link #commit()} is called. Passing {@code null}
|
||||
* as value isn't allowed.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
Editor remove(String name);
|
||||
/**
|
||||
* Commits changes. It can be called only once.
|
||||
*
|
||||
* @throws CacheException If the commit fails.
|
||||
*/
|
||||
void commit() throws CacheException;
|
||||
}
|
||||
|
||||
/** Returns an editor to change metadata values. */
|
||||
Editor edit();
|
||||
|
||||
/**
|
||||
* Returns a metadata value.
|
||||
*
|
||||
|
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* 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 com.google.android.exoplayer2.util.Assertions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Defines multiple mutations on metadata value which are applied atomically. */
|
||||
public class ContentMetadataMutations {
|
||||
|
||||
private final Map<String, Object> editedValues;
|
||||
private final List<String> removedValues;
|
||||
|
||||
/** Constructs a DefaultMetadataMutations. */
|
||||
public ContentMetadataMutations() {
|
||||
editedValues = new HashMap<>();
|
||||
removedValues = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mutation to set a metadata value. Passing {@code null} as {@code value} isn't allowed.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
public ContentMetadataMutations set(String name, String value) {
|
||||
return checkAndSet(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mutation to set a metadata value.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
public ContentMetadataMutations set(String name, long value) {
|
||||
return checkAndSet(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mutation to set a metadata value. Passing {@code null} as {@code value} isn't allowed.
|
||||
* {@code value} byte array shouldn't be modified after passed to this method.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
public ContentMetadataMutations set(String name, byte[] value) {
|
||||
return checkAndSet(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mutation to remove a metadata value.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @return This Editor instance, for convenience.
|
||||
*/
|
||||
public ContentMetadataMutations remove(String name) {
|
||||
removedValues.add(name);
|
||||
editedValues.remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of names of metadata values to be removed. The returned array shouldn't be
|
||||
* modified.
|
||||
*/
|
||||
public List<String> getRemovedValues() {
|
||||
return removedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of metadata name, value pairs to be set. The returned map and the values in it
|
||||
* shouldn't be modified.
|
||||
*/
|
||||
public Map<String, Object> getEditedValues() {
|
||||
return editedValues;
|
||||
}
|
||||
|
||||
private ContentMetadataMutations checkAndSet(String name, Object value) {
|
||||
editedValues.put(name, Assertions.checkNotNull(value));
|
||||
removedValues.remove(name);
|
||||
return this;
|
||||
}
|
||||
}
|
@ -15,59 +15,30 @@
|
||||
*/
|
||||
package com.google.android.exoplayer2.upstream.cache;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** Default implementation of {@link ContentMetadata}. Values are stored as byte arrays. */
|
||||
public final class DefaultContentMetadata implements ContentMetadata {
|
||||
|
||||
/** Listener for metadata change event. */
|
||||
public interface ChangeListener {
|
||||
/**
|
||||
* Called when any metadata value is changed or removed.
|
||||
*
|
||||
* @param contentMetadata The reporting instance.
|
||||
* @param metadataValues All metadata name, value pairs. It shouldn't be accessed out of this
|
||||
* method.
|
||||
*/
|
||||
void onChange(DefaultContentMetadata contentMetadata, Map<String, byte[]> metadataValues)
|
||||
throws CacheException;
|
||||
}
|
||||
|
||||
private final Map<String, byte[]> metadata;
|
||||
@Nullable private ChangeListener changeListener;
|
||||
|
||||
/** Constructs an empty {@link DefaultContentMetadata}. */
|
||||
public DefaultContentMetadata() {
|
||||
this.metadata = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link DefaultContentMetadata} using name, value pairs data which was passed to
|
||||
* {@link ChangeListener#onChange(DefaultContentMetadata, Map)}.
|
||||
*
|
||||
* @param metadata Initial name, value pairs.
|
||||
* Constructs a {@link DefaultContentMetadata} by copying metadata values from {@code other} and
|
||||
* applying {@code mutations}.
|
||||
*/
|
||||
public DefaultContentMetadata(Map<String, byte[]> metadata) {
|
||||
this.metadata = new HashMap<>(metadata);
|
||||
}
|
||||
|
||||
/** Sets a {@link ChangeListener}. This method can be called once. */
|
||||
public void setChangeListener(ChangeListener changeListener) {
|
||||
Assertions.checkState(this.changeListener == null);
|
||||
this.changeListener = Assertions.checkNotNull(changeListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Editor edit() {
|
||||
return new EditorImpl();
|
||||
public DefaultContentMetadata(DefaultContentMetadata other, ContentMetadataMutations mutations) {
|
||||
this.metadata = new HashMap<>(other.metadata);
|
||||
applyMutations(mutations);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -112,60 +83,28 @@ public final class DefaultContentMetadata implements ContentMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
private void apply(ArrayList<String> removedValues, Map<String, byte[]> editedValues)
|
||||
throws CacheException {
|
||||
synchronized (metadata) {
|
||||
for (int i = 0; i < removedValues.size(); i++) {
|
||||
metadata.remove(removedValues.get(i));
|
||||
}
|
||||
metadata.putAll(editedValues);
|
||||
if (changeListener != null) {
|
||||
changeListener.onChange(this, Collections.unmodifiableMap(metadata));
|
||||
}
|
||||
private void applyMutations(ContentMetadataMutations mutations) {
|
||||
List<String> removedValues = mutations.getRemovedValues();
|
||||
for (int i = 0; i < removedValues.size(); i++) {
|
||||
metadata.remove(removedValues.get(i));
|
||||
}
|
||||
Map<String, Object> editedValues = mutations.getEditedValues();
|
||||
for (String name : editedValues.keySet()) {
|
||||
Object value = editedValues.get(name);
|
||||
metadata.put(name, getBytes(value));
|
||||
}
|
||||
}
|
||||
|
||||
private class EditorImpl implements Editor {
|
||||
|
||||
private final Map<String, byte[]> editedValues;
|
||||
private final ArrayList<String> removedValues;
|
||||
|
||||
private EditorImpl() {
|
||||
editedValues = new HashMap<>();
|
||||
removedValues = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor set(String name, String value) {
|
||||
set(name, value.getBytes());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor set(String name, long value) {
|
||||
set(name, ByteBuffer.allocate(8).putLong(value).array());
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor set(String name, byte[] value) {
|
||||
editedValues.put(name, Assertions.checkNotNull(value));
|
||||
removedValues.remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Editor remove(String name) {
|
||||
removedValues.add(name);
|
||||
editedValues.remove(name);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() throws CacheException {
|
||||
apply(removedValues, editedValues);
|
||||
removedValues.clear();
|
||||
editedValues.clear();
|
||||
private static byte[] getBytes(Object value) {
|
||||
if (value instanceof Long) {
|
||||
return ByteBuffer.allocate(8).putLong((Long) value).array();
|
||||
} else if (value instanceof String) {
|
||||
return ((String) value).getBytes(Charset.forName(C.UTF8_NAME));
|
||||
} else if (value instanceof byte[]) {
|
||||
return (byte[]) value;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,15 +17,6 @@ package com.google.android.exoplayer2.upstream.cache;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.upstream.cache.Cache.CacheException;
|
||||
import com.google.android.exoplayer2.upstream.cache.ContentMetadata.Editor;
|
||||
import com.google.android.exoplayer2.upstream.cache.DefaultContentMetadata.ChangeListener;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -36,11 +27,9 @@ import org.robolectric.RobolectricTestRunner;
|
||||
public class DefaultContentMetadataTest {
|
||||
|
||||
private DefaultContentMetadata contentMetadata;
|
||||
private FakeChangeListener changeListener;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
changeListener = new FakeChangeListener();
|
||||
contentMetadata = createAbstractContentMetadata();
|
||||
}
|
||||
|
||||
@ -67,124 +56,85 @@ public class DefaultContentMetadataTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditReturnsAnEditor() throws Exception {
|
||||
assertThat(contentMetadata.edit()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditReturnsAnotherEditorEveryTime() throws Exception {
|
||||
assertThat(contentMetadata.edit()).isNotEqualTo(contentMetadata.edit());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommitWithoutEditDoesNotFail() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.commit();
|
||||
public void testEmptyMutationDoesNotFail() throws Exception {
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
new DefaultContentMetadata(new DefaultContentMetadata(), mutations);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNewMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "value");
|
||||
editor.commit();
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
mutations.set("metadata name", "value");
|
||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNewIntMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", 5);
|
||||
editor.commit();
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
mutations.set("metadata name", 5);
|
||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||
assertThat(contentMetadata.get("metadata name", 0)).isEqualTo(5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddNewByteArrayMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
byte[] value = {1, 2, 3};
|
||||
editor.set("metadata name", value);
|
||||
editor.commit();
|
||||
mutations.set("metadata name", value);
|
||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||
assertThat(contentMetadata.get("metadata name", new byte[] {})).isEqualTo(value);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewMetadataNotWrittenBeforeCommitted() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "value");
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
mutations.set("metadata name", "value");
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEditMetadata() throws Exception {
|
||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "edited value");
|
||||
editor.commit();
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
mutations.set("metadata name", "edited value");
|
||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("edited value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveMetadata() throws Exception {
|
||||
contentMetadata = createAbstractContentMetadata("metadata name", "value");
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.remove("metadata name");
|
||||
editor.commit();
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
mutations.remove("metadata name");
|
||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAndRemoveMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "value");
|
||||
editor.remove("metadata name");
|
||||
editor.commit();
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
mutations.set("metadata name", "value");
|
||||
mutations.remove("metadata name");
|
||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("default value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveAndAddMetadata() throws Exception {
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.remove("metadata name");
|
||||
editor.set("metadata name", "value");
|
||||
editor.commit();
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
mutations.remove("metadata name");
|
||||
mutations.set("metadata name", "value");
|
||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||
assertThat(contentMetadata.get("metadata name", "default value")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnChangeIsCalledWhenMetadataEdited() throws Exception {
|
||||
contentMetadata =
|
||||
createAbstractContentMetadata(
|
||||
"metadata name", "value", "metadata name2", "value2", "metadata name3", "value3");
|
||||
Editor editor = contentMetadata.edit();
|
||||
editor.set("metadata name", "edited value");
|
||||
editor.remove("metadata name2");
|
||||
editor.commit();
|
||||
assertThat(changeListener.remainingValues).containsExactly("metadata name", "metadata name3");
|
||||
}
|
||||
|
||||
private DefaultContentMetadata createAbstractContentMetadata(String... pairs) {
|
||||
assertThat(pairs.length % 2).isEqualTo(0);
|
||||
HashMap<String, byte[]> map = new HashMap<>();
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
for (int i = 0; i < pairs.length; i += 2) {
|
||||
map.put(pairs[i], getBytes(pairs[i + 1]));
|
||||
}
|
||||
DefaultContentMetadata metadata = new DefaultContentMetadata(Collections.unmodifiableMap(map));
|
||||
metadata.setChangeListener(changeListener);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static byte[] getBytes(String value) {
|
||||
return value.getBytes(Charset.forName(C.UTF8_NAME));
|
||||
}
|
||||
|
||||
private static class FakeChangeListener implements ChangeListener {
|
||||
|
||||
private ArrayList<String> remainingValues;
|
||||
|
||||
@Override
|
||||
public void onChange(DefaultContentMetadata contentMetadata, Map<String, byte[]> metadataValues)
|
||||
throws CacheException {
|
||||
remainingValues = new ArrayList<>(metadataValues.keySet());
|
||||
mutations.set(pairs[i], pairs[i + 1]);
|
||||
}
|
||||
return new DefaultContentMetadata(new DefaultContentMetadata(), mutations);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user