mirror of
https://github.com/samsonjs/media.git
synced 2026-06-28 05:29:33 +00:00
Make CachedContent serialize whole metadata
------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=189166039
This commit is contained in:
parent
42d7a319cd
commit
10a48e778e
6 changed files with 200 additions and 89 deletions
|
|
@ -29,6 +29,25 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
|||
0, 0, 0, 0, 0, 0, 10, 0, // original_content_length
|
||||
(byte) 0xF6, (byte) 0xFB, 0x50, 0x41 // hashcode_of_CachedContent_array
|
||||
};
|
||||
|
||||
private final byte[] testIndexV2File = {
|
||||
0, 0, 0, 2, // version
|
||||
0, 0, 0, 0, // flags
|
||||
0, 0, 0, 2, // number_of_CachedContent
|
||||
0, 0, 0, 1, // cache_id
|
||||
0, 5, 65, 66, 67, 68, 69, // cache_key "ABCDE"
|
||||
0, 0, 0, 1, // metadata count
|
||||
0, 7, 101, 120, 111, 95, 108, 101, 110, // "exo_len"
|
||||
0, 0, 0, 8, // value length
|
||||
0, 0, 0, 0, 0, 0, 0, 10, // original_content_length
|
||||
0, 0, 0, 0, // cache_id
|
||||
0, 5, 75, 76, 77, 78, 79, // cache_key "KLMNO"
|
||||
0, 0, 0, 1, // metadata count
|
||||
0, 7, 101, 120, 111, 95, 108, 101, 110, // "exo_len"
|
||||
0, 0, 0, 8, // value length
|
||||
0, 0, 0, 0, 0, 0, 10, 0, // original_content_length
|
||||
(byte) 0x42, (byte) 0x4A, (byte) 0x4F, (byte) 0x6F // hashcode_of_CachedContent_array
|
||||
};
|
||||
private CachedContentIndex index;
|
||||
private File cacheDir;
|
||||
|
||||
|
|
@ -51,8 +70,7 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
|||
final String key3 = "key3";
|
||||
|
||||
// Add two CachedContents with add methods
|
||||
CachedContent cachedContent1 = new CachedContent(5, key1);
|
||||
index.addNew(cachedContent1);
|
||||
CachedContent cachedContent1 = index.getOrAdd(key1);
|
||||
CachedContent cachedContent2 = index.getOrAdd(key2);
|
||||
assertThat(cachedContent1.id != cachedContent2.id).isTrue();
|
||||
|
||||
|
|
@ -88,7 +106,7 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
|||
assertThat(cacheSpanFile.exists()).isTrue();
|
||||
|
||||
// test removeEmpty()
|
||||
index.addNew(cachedContent2);
|
||||
index.getOrAdd(key2);
|
||||
index.removeEmpty();
|
||||
assertThat(index.get(key1)).isEqualTo(cachedContent1);
|
||||
assertThat(index.get(key2)).isNull();
|
||||
|
|
@ -112,25 +130,33 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
|||
assertThat(index.getContentLength("KLMNO")).isEqualTo(2560);
|
||||
}
|
||||
|
||||
public void testStoreV1() throws Exception {
|
||||
CachedContent cachedContent1 = new CachedContent(2, "KLMNO");
|
||||
public void testLoadV2() throws Exception {
|
||||
FileOutputStream fos = new FileOutputStream(new File(cacheDir, CachedContentIndex.FILE_NAME));
|
||||
fos.write(testIndexV2File);
|
||||
fos.close();
|
||||
|
||||
index.load();
|
||||
assertThat(index.getAll()).hasSize(2);
|
||||
assertThat(index.assignIdForKey("ABCDE")).isEqualTo(1);
|
||||
assertThat(index.getContentLength("ABCDE")).isEqualTo(10);
|
||||
assertThat(index.assignIdForKey("KLMNO")).isEqualTo(0);
|
||||
assertThat(index.getContentLength("KLMNO")).isEqualTo(2560);
|
||||
}
|
||||
|
||||
public void testStore() throws Exception {
|
||||
CachedContent cachedContent1 = index.getOrAdd("KLMNO");
|
||||
cachedContent1.setLength(2560);
|
||||
index.addNew(cachedContent1);
|
||||
CachedContent cachedContent2 = new CachedContent(5, "ABCDE");
|
||||
CachedContent cachedContent2 = index.getOrAdd("ABCDE");
|
||||
cachedContent2.setLength(10);
|
||||
index.addNew(cachedContent2);
|
||||
|
||||
index.store();
|
||||
|
||||
byte[] buffer = new byte[testIndexV1File.length];
|
||||
FileInputStream fos = new FileInputStream(new File(cacheDir, CachedContentIndex.FILE_NAME));
|
||||
assertThat(fos.read(buffer)).isEqualTo(testIndexV1File.length);
|
||||
assertThat(fos.read()).isEqualTo(-1);
|
||||
fos.close();
|
||||
byte[] buffer = Util.toByteArray(fos);
|
||||
|
||||
// TODO: The order of the CachedContent stored in index file isn't defined so this test may fail
|
||||
// on a different implementation of the underlying set
|
||||
assertThat(buffer).isEqualTo(testIndexV1File);
|
||||
assertThat(buffer).isEqualTo(testIndexV2File);
|
||||
}
|
||||
|
||||
public void testAssignIdForKeyAndGetKeyForId() throws Exception {
|
||||
|
|
@ -214,14 +240,13 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
|||
|
||||
// Test multiple store() calls
|
||||
CachedContentIndex index = new CachedContentIndex(cacheDir, key);
|
||||
index.addNew(new CachedContent(15, "key3"));
|
||||
index.getOrAdd("key3");
|
||||
index.store();
|
||||
assertStoredAndLoadedEqual(index, new CachedContentIndex(cacheDir, key));
|
||||
}
|
||||
|
||||
public void testRemoveEmptyNotLockedCachedContent() throws Exception {
|
||||
CachedContent cachedContent = new CachedContent(5, "key1");
|
||||
index.addNew(cachedContent);
|
||||
CachedContent cachedContent = index.getOrAdd("key1");
|
||||
|
||||
index.maybeRemove(cachedContent.key);
|
||||
|
||||
|
|
@ -229,8 +254,7 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
|||
}
|
||||
|
||||
public void testCantRemoveNotEmptyCachedContent() throws Exception {
|
||||
CachedContent cachedContent = new CachedContent(5, "key1");
|
||||
index.addNew(cachedContent);
|
||||
CachedContent cachedContent = index.getOrAdd("key1");
|
||||
File cacheSpanFile =
|
||||
SimpleCacheSpanTest.createCacheSpanFile(cacheDir, cachedContent.id, 10, 20, 30);
|
||||
SimpleCacheSpan span = SimpleCacheSpan.createCacheEntry(cacheSpanFile, index);
|
||||
|
|
@ -242,9 +266,8 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
|||
}
|
||||
|
||||
public void testCantRemoveLockedCachedContent() throws Exception {
|
||||
CachedContent cachedContent = new CachedContent(5, "key1");
|
||||
CachedContent cachedContent = index.getOrAdd("key1");
|
||||
cachedContent.setLocked(true);
|
||||
index.addNew(cachedContent);
|
||||
|
||||
index.maybeRemove(cachedContent.key);
|
||||
|
||||
|
|
@ -253,7 +276,7 @@ public class CachedContentIndexTest extends InstrumentationTestCase {
|
|||
|
||||
private void assertStoredAndLoadedEqual(CachedContentIndex index, CachedContentIndex index2)
|
||||
throws IOException {
|
||||
index.addNew(new CachedContent(5, "key1"));
|
||||
index.getOrAdd("key1");
|
||||
index.getOrAdd("key2");
|
||||
index.store();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,10 @@ import java.util.TreeSet;
|
|||
*/
|
||||
/*package*/ final class CachedContent {
|
||||
|
||||
private static final int VERSION_METADATA_INTRODUCED = 2;
|
||||
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. */
|
||||
|
|
@ -44,15 +46,22 @@ import java.util.TreeSet;
|
|||
/**
|
||||
* Reads an instance from a {@link DataInputStream}.
|
||||
*
|
||||
* @param version Version of the encoded data.
|
||||
* @param input Input stream containing values needed to initialize CachedContent instance.
|
||||
* @throws IOException If an error occurs during reading values.
|
||||
*/
|
||||
public static CachedContent readFromStream(DataInputStream input) throws IOException {
|
||||
public static CachedContent readFromStream(int version, 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);
|
||||
CachedContent cachedContent;
|
||||
if (version < VERSION_METADATA_INTRODUCED) {
|
||||
cachedContent = new CachedContent(id, key);
|
||||
long length = input.readLong();
|
||||
cachedContent.setLength(length);
|
||||
} else {
|
||||
cachedContent = new CachedContent(id, key, DefaultContentMetadata.readFromStream(input));
|
||||
}
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
|
|
@ -63,9 +72,13 @@ import java.util.TreeSet;
|
|||
* @param key The cache stream key.
|
||||
*/
|
||||
public CachedContent(int id, String key) {
|
||||
this(id, key, new DefaultContentMetadata());
|
||||
}
|
||||
|
||||
private CachedContent(int id, String key, DefaultContentMetadata metadata) {
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
this.metadata = new DefaultContentMetadata();
|
||||
this.metadata = metadata;
|
||||
this.cachedSpans = new TreeSet<>();
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +91,7 @@ import java.util.TreeSet;
|
|||
public void writeToStream(DataOutputStream output) throws IOException {
|
||||
output.writeInt(id);
|
||||
output.writeUTF(key);
|
||||
output.writeLong(getLength());
|
||||
metadata.writeToStream(output);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -202,12 +215,19 @@ import java.util.TreeSet;
|
|||
return false;
|
||||
}
|
||||
|
||||
/** Calculates a hash code for the header of this {@code CachedContent}. */
|
||||
public int headerHashCode() {
|
||||
long length = getLength();
|
||||
/**
|
||||
* Calculates a hash code for the header of this {@code CachedContent} which is compatible with
|
||||
* the index file with {@code version}.
|
||||
*/
|
||||
public int headerHashCode(int version) {
|
||||
int result = id;
|
||||
result = 31 * result + key.hashCode();
|
||||
result = 31 * result + (int) (length ^ (length >>> 32));
|
||||
if (version < VERSION_METADATA_INTRODUCED) {
|
||||
long length = getLength();
|
||||
result = 31 * result + (int) (length ^ (length >>> 32));
|
||||
} else {
|
||||
result = 31 * result + metadata.hashCode();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
|
||||
public static final String FILE_NAME = "cached_content_index.exi";
|
||||
|
||||
private static final int VERSION = 1;
|
||||
private static final int VERSION = 2;
|
||||
|
||||
private static final int FLAG_ENCRYPTED_INDEX = 1;
|
||||
|
||||
|
|
@ -139,10 +139,7 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
*/
|
||||
public CachedContent getOrAdd(String key) {
|
||||
CachedContent cachedContent = keyToContent.get(key);
|
||||
if (cachedContent == null) {
|
||||
cachedContent = addNew(key, C.LENGTH_UNSET);
|
||||
}
|
||||
return cachedContent;
|
||||
return cachedContent == null ? addNew(key) : cachedContent;
|
||||
}
|
||||
|
||||
/** Returns a CachedContent instance with the given key or null if there isn't one. */
|
||||
|
|
@ -205,14 +202,10 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
* one already with the given key.
|
||||
*/
|
||||
public void setContentLength(String key, long length) {
|
||||
CachedContent cachedContent = get(key);
|
||||
if (cachedContent != null) {
|
||||
if (cachedContent.getLength() != length) {
|
||||
cachedContent.setLength(length);
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
addNew(key, length);
|
||||
CachedContent cachedContent = getOrAdd(key);
|
||||
if (cachedContent.getLength() != length) {
|
||||
cachedContent.setLength(length);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -231,8 +224,7 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
InputStream inputStream = new BufferedInputStream(atomicFile.openRead());
|
||||
input = new DataInputStream(inputStream);
|
||||
int version = input.readInt();
|
||||
if (version != VERSION) {
|
||||
// Currently there is no other version
|
||||
if (version < 0 || version > VERSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -257,9 +249,9 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
int count = input.readInt();
|
||||
int hashCode = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
CachedContent cachedContent = CachedContent.readFromStream(input);
|
||||
CachedContent cachedContent = CachedContent.readFromStream(version, input);
|
||||
add(cachedContent);
|
||||
hashCode += cachedContent.headerHashCode();
|
||||
hashCode += cachedContent.headerHashCode(version);
|
||||
}
|
||||
if (input.readInt() != hashCode) {
|
||||
return false;
|
||||
|
|
@ -310,7 +302,7 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
int hashCode = 0;
|
||||
for (CachedContent cachedContent : keyToContent.values()) {
|
||||
cachedContent.writeToStream(output);
|
||||
hashCode += cachedContent.headerHashCode();
|
||||
hashCode += cachedContent.headerHashCode(VERSION);
|
||||
}
|
||||
output.writeInt(hashCode);
|
||||
atomicFile.endWrite(output);
|
||||
|
|
@ -324,25 +316,19 @@ import javax.crypto.spec.SecretKeySpec;
|
|||
}
|
||||
}
|
||||
|
||||
private CachedContent addNew(String key) {
|
||||
int id = getNewId(idToKey);
|
||||
CachedContent cachedContent = new CachedContent(id, key);
|
||||
add(cachedContent);
|
||||
changed = true;
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
private void add(CachedContent cachedContent) {
|
||||
keyToContent.put(cachedContent.key, cachedContent);
|
||||
idToKey.put(cachedContent.id, cachedContent.key);
|
||||
}
|
||||
|
||||
/** Adds the given CachedContent to the index. */
|
||||
/*package*/ void addNew(CachedContent cachedContent) {
|
||||
add(cachedContent);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
private CachedContent addNew(String key, long length) {
|
||||
int id = getNewId(idToKey);
|
||||
CachedContent cachedContent = new CachedContent(id, key);
|
||||
cachedContent.setLength(length);
|
||||
addNew(cachedContent);
|
||||
return cachedContent;
|
||||
}
|
||||
|
||||
private static Cipher getCipher() throws NoSuchPaddingException, NoSuchAlgorithmException {
|
||||
// Workaround for https://issuetracker.google.com/issues/36976726
|
||||
if (Util.SDK_INT == 18) {
|
||||
|
|
|
|||
|
|
@ -17,11 +17,17 @@ package com.google.android.exoplayer2.upstream.cache;
|
|||
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/** Defines multiple mutations on metadata value which are applied atomically. */
|
||||
/**
|
||||
* Defines multiple mutations on metadata value which are applied atomically. This class isn't
|
||||
* thread safe.
|
||||
*/
|
||||
public class ContentMetadataMutations {
|
||||
|
||||
private final Map<String, Object> editedValues;
|
||||
|
|
@ -34,7 +40,8 @@ public class ContentMetadataMutations {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a mutation to set a metadata value. Passing {@code null} as {@code value} isn't allowed.
|
||||
* Adds a mutation to set a metadata value. Passing {@code null} as {@code name} or {@code value}
|
||||
* isn't allowed.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
|
|
@ -45,7 +52,7 @@ public class ContentMetadataMutations {
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds a mutation to set a metadata value.
|
||||
* Adds a mutation to set a metadata value. Passing {@code null} as {@code name} isn't allowed.
|
||||
*
|
||||
* @param name The name of the metadata value.
|
||||
* @param value The value to be set.
|
||||
|
|
@ -56,15 +63,15 @@ public class ContentMetadataMutations {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Adds a mutation to set a metadata value. Passing {@code null} as {@code name} or {@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, byte[] value) {
|
||||
return checkAndSet(name, value);
|
||||
return checkAndSet(name, Arrays.copyOf(value, value.length));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -79,24 +86,26 @@ public class ContentMetadataMutations {
|
|||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of names of metadata values to be removed. The returned array shouldn't be
|
||||
* modified.
|
||||
*/
|
||||
/** Returns a list of names of metadata values to be removed. */
|
||||
public List<String> getRemovedValues() {
|
||||
return removedValues;
|
||||
return Collections.unmodifiableList(new ArrayList<>(removedValues));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map of metadata name, value pairs to be set. The returned map and the values in it
|
||||
* shouldn't be modified.
|
||||
*/
|
||||
/** Returns a map of metadata name, value pairs to be set. Values are copied. */
|
||||
public Map<String, Object> getEditedValues() {
|
||||
return editedValues;
|
||||
HashMap<String, Object> hashMap = new HashMap<>(editedValues);
|
||||
for (Entry<String, Object> entry : hashMap.entrySet()) {
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof byte[]) {
|
||||
byte[] bytes = (byte[]) value;
|
||||
entry.setValue(Arrays.copyOf(bytes, bytes.length));
|
||||
}
|
||||
}
|
||||
return Collections.unmodifiableMap(hashMap);
|
||||
}
|
||||
|
||||
private ContentMetadataMutations checkAndSet(String name, Object value) {
|
||||
editedValues.put(name, Assertions.checkNotNull(value));
|
||||
editedValues.put(Assertions.checkNotNull(name), Assertions.checkNotNull(value));
|
||||
removedValues.remove(name);
|
||||
return this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import java.io.DataOutputStream;
|
|||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
|
@ -31,6 +32,7 @@ import java.util.Map.Entry;
|
|||
public final class DefaultContentMetadata implements ContentMetadata {
|
||||
|
||||
private static final int MAX_VALUE_LENGTH = 10 * 1024 * 1024;
|
||||
private int hashCode;
|
||||
|
||||
/**
|
||||
* Deserializes a {@link DefaultContentMetadata} from the given input stream.
|
||||
|
|
@ -93,7 +95,8 @@ public final class DefaultContentMetadata implements ContentMetadata {
|
|||
@Override
|
||||
public final byte[] get(String name, byte[] defaultValue) {
|
||||
if (metadata.containsKey(name)) {
|
||||
return metadata.get(name);
|
||||
byte[] bytes = metadata.get(name);
|
||||
return Arrays.copyOf(bytes, bytes.length);
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
|
|
@ -124,6 +127,41 @@ public final class DefaultContentMetadata implements ContentMetadata {
|
|||
return metadata.containsKey(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
DefaultContentMetadata that = (DefaultContentMetadata) o;
|
||||
Map<String, byte[]> otherMetadata = that.metadata;
|
||||
if (metadata.size() != otherMetadata.size()) {
|
||||
return false;
|
||||
}
|
||||
for (Entry<String, byte[]> entry : metadata.entrySet()) {
|
||||
byte[] value = entry.getValue();
|
||||
byte[] otherValue = otherMetadata.get(entry.getKey());
|
||||
if (!Arrays.equals(value, otherValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
if (hashCode == 0) {
|
||||
int result = 0;
|
||||
for (Entry<String, byte[]> entry : metadata.entrySet()) {
|
||||
result += entry.getKey().hashCode() ^ Arrays.hashCode(entry.getValue());
|
||||
}
|
||||
hashCode = result;
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
private static Map<String, byte[]> applyMutations(
|
||||
Map<String, byte[]> otherMetadata, ContentMetadataMutations mutations) {
|
||||
HashMap<String, byte[]> metadata = new HashMap<>(otherMetadata);
|
||||
|
|
@ -154,7 +192,7 @@ public final class DefaultContentMetadata implements ContentMetadata {
|
|||
} else if (value instanceof byte[]) {
|
||||
return (byte[]) value;
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -135,12 +135,10 @@ public class DefaultContentMetadataTest {
|
|||
|
||||
@Test
|
||||
public void testSerializeDeserialize() throws Exception {
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
mutations.set("metadata1 name", "value");
|
||||
mutations.set("metadata2 name", 12345);
|
||||
byte[] metadata3 = {1, 2, 3};
|
||||
mutations.set("metadata3 name", metadata3);
|
||||
contentMetadata = new DefaultContentMetadata(contentMetadata, mutations);
|
||||
contentMetadata =
|
||||
createContentMetadata(
|
||||
"metadata1 name", "value", "metadata2 name", 12345, "metadata3 name", metadata3);
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
contentMetadata.writeToStream(new DataOutputStream(outputStream));
|
||||
|
|
@ -153,11 +151,48 @@ public class DefaultContentMetadataTest {
|
|||
assertThat(contentMetadata2.get("metadata3 name", new byte[] {})).isEqualTo(metadata3);
|
||||
}
|
||||
|
||||
private DefaultContentMetadata createContentMetadata(String... pairs) {
|
||||
@Test
|
||||
public void testEqualsStringValues() throws Exception {
|
||||
DefaultContentMetadata metadata1 = createContentMetadata("metadata1", "value");
|
||||
DefaultContentMetadata metadata2 = createContentMetadata("metadata1", "value");
|
||||
assertThat(metadata1).isEqualTo(metadata2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEquals() throws Exception {
|
||||
DefaultContentMetadata metadata1 =
|
||||
createContentMetadata(
|
||||
"metadata1", "value", "metadata2", 12345, "metadata3", new byte[] {1, 2, 3});
|
||||
DefaultContentMetadata metadata2 =
|
||||
createContentMetadata(
|
||||
"metadata2", 12345, "metadata3", new byte[] {1, 2, 3}, "metadata1", "value");
|
||||
assertThat(metadata1).isEqualTo(metadata2);
|
||||
assertThat(metadata1.hashCode()).isEqualTo(metadata2.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotEquals() throws Exception {
|
||||
DefaultContentMetadata metadata1 = createContentMetadata("metadata1", new byte[] {1, 2, 3});
|
||||
DefaultContentMetadata metadata2 = createContentMetadata("metadata1", new byte[] {3, 2, 1});
|
||||
assertThat(metadata1).isNotEqualTo(metadata2);
|
||||
assertThat(metadata1.hashCode()).isNotEqualTo(metadata2.hashCode());
|
||||
}
|
||||
|
||||
private DefaultContentMetadata createContentMetadata(Object... pairs) {
|
||||
assertThat(pairs.length % 2).isEqualTo(0);
|
||||
ContentMetadataMutations mutations = new ContentMetadataMutations();
|
||||
for (int i = 0; i < pairs.length; i += 2) {
|
||||
mutations.set(pairs[i], pairs[i + 1]);
|
||||
String name = (String) pairs[i];
|
||||
Object value = pairs[i + 1];
|
||||
if (value instanceof String) {
|
||||
mutations.set(name, (String) value);
|
||||
} else if (value instanceof byte[]) {
|
||||
mutations.set(name, (byte[]) value);
|
||||
} else if (value instanceof Number) {
|
||||
mutations.set(name, ((Number) value).longValue());
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
return new DefaultContentMetadata(new DefaultContentMetadata(), mutations);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue