mirror of
https://github.com/androidx/media.git
synced 2025-05-21 23:56:32 +08:00
Add test for FullSegmentEncryptionKeyCache
Pull it into a top-level package private class at the same time
As suggested in 0ba91811d1
PiperOrigin-RevId: 275870515
This commit is contained in:
parent
c139281119
commit
3e6fe45885
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.source.hls;
|
||||
|
||||
import android.net.Uri;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* LRU cache that holds up to {@code maxSize} full-segment-encryption keys. Which each addition,
|
||||
* once the cache's size exceeds {@code maxSize}, the oldest item (according to insertion order) is
|
||||
* removed.
|
||||
*/
|
||||
/* package */ final class FullSegmentEncryptionKeyCache {
|
||||
|
||||
private final LinkedHashMap<Uri, byte[]> backingMap;
|
||||
|
||||
public FullSegmentEncryptionKeyCache(int maxSize) {
|
||||
backingMap =
|
||||
new LinkedHashMap<Uri, byte[]>(
|
||||
/* initialCapacity= */ maxSize + 1, /* loadFactor= */ 1, /* accessOrder= */ false) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Uri, byte[]> eldest) {
|
||||
return size() > maxSize;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code encryptionKey} cached against this {@code uri}, or null if {@code uri} is
|
||||
* null or not present in the cache.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] get(@Nullable Uri uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
return backingMap.get(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an entry into the cache.
|
||||
*
|
||||
* @throws NullPointerException if {@code uri} or {@code encryptionKey} are null.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] put(Uri uri, byte[] encryptionKey) {
|
||||
return backingMap.put(Assertions.checkNotNull(uri), Assertions.checkNotNull(encryptionKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@code uri} is present in the cache.
|
||||
*
|
||||
* @throws NullPointerException if {@code uri} is null.
|
||||
*/
|
||||
public boolean containsUri(Uri uri) {
|
||||
return backingMap.containsKey(Assertions.checkNotNull(uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@code uri} from the cache. If {@code uri} was present in the cahce, this returns the
|
||||
* corresponding {@code encryptionKey}, otherwise null.
|
||||
*
|
||||
* @throws NullPointerException if {@code uri} is null.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] remove(Uri uri) {
|
||||
return backingMap.remove(Assertions.checkNotNull(uri));
|
||||
}
|
||||
}
|
@ -41,9 +41,7 @@ import com.google.android.exoplayer2.util.UriUtil;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
|
||||
/** Source of Hls (possibly adaptive) chunks. */
|
||||
@ -142,7 +140,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
this.playlistFormats = playlistFormats;
|
||||
this.timestampAdjusterProvider = timestampAdjusterProvider;
|
||||
this.muxedCaptionFormats = muxedCaptionFormats;
|
||||
keyCache = new FullSegmentEncryptionKeyCache();
|
||||
keyCache = new FullSegmentEncryptionKeyCache(KEY_CACHE_SIZE);
|
||||
scratchSpace = Util.EMPTY_BYTE_ARRAY;
|
||||
liveEdgeInPeriodTimeUs = C.TIME_UNSET;
|
||||
mediaDataSource = dataSourceFactory.createDataSource(C.DATA_TYPE_MEDIA);
|
||||
@ -667,69 +665,4 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
return segmentStartTimeInPeriodUs + segment.durationUs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LRU cache that holds up to {@link #KEY_CACHE_SIZE} full-segment-encryption keys. Which each
|
||||
* addition, once the cache's size exceeds {@link #KEY_CACHE_SIZE}, the oldest item (according to
|
||||
* insertion order) is removed.
|
||||
*/
|
||||
private static final class FullSegmentEncryptionKeyCache {
|
||||
|
||||
private final LinkedHashMap<Uri, byte[]> backingMap;
|
||||
|
||||
public FullSegmentEncryptionKeyCache() {
|
||||
backingMap =
|
||||
new LinkedHashMap<Uri, byte[]>(
|
||||
/* initialCapacity= */ KEY_CACHE_SIZE + 1,
|
||||
/* loadFactor= */ 1,
|
||||
/* accessOrder= */ false) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Uri, byte[]> eldest) {
|
||||
return size() > KEY_CACHE_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code encryptionKey} cached against this {@code uri}, or null if {@code uri} is
|
||||
* null or not present in the cache.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] get(@Nullable Uri uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
return backingMap.get(uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an entry into the cache.
|
||||
*
|
||||
* @throws NullPointerException if {@code uri} or {@code encryptionKey} are null.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] put(Uri uri, byte[] encryptionKey) {
|
||||
return backingMap.put(Assertions.checkNotNull(uri), Assertions.checkNotNull(encryptionKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if {@code uri} is present in the cache.
|
||||
*
|
||||
* @throws NullPointerException if {@code uri} is null.
|
||||
*/
|
||||
public boolean containsUri(Uri uri) {
|
||||
return backingMap.containsKey(Assertions.checkNotNull(uri));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes {@code uri} from the cache. If {@code uri} was present in the cahce, this returns the
|
||||
* corresponding {@code encryptionKey}, otherwise null.
|
||||
*
|
||||
* @throws NullPointerException if {@code uri} is null.
|
||||
*/
|
||||
@Nullable
|
||||
public byte[] remove(Uri uri) {
|
||||
return backingMap.remove(Assertions.checkNotNull(uri));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.source.hls;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.net.Uri;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
/** Tests for {@link FullSegmentEncryptionKeyCache}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FullSegmentEncryptionKeyCacheTest {
|
||||
|
||||
private final Uri firstUri = Uri.parse("https://www.google.com");
|
||||
private final Uri secondUri = Uri.parse("https://www.abc.xyz");
|
||||
private final byte[] encryptionKey = {5, 6, 7, 8};
|
||||
|
||||
@Test
|
||||
public void putThenGetAndContains() {
|
||||
FullSegmentEncryptionKeyCache cache = new FullSegmentEncryptionKeyCache(/* maxSize= */ 5);
|
||||
cache.put(firstUri, encryptionKey);
|
||||
assertThat(cache.get(firstUri)).isEqualTo(encryptionKey);
|
||||
assertThat(cache.get(secondUri)).isNull();
|
||||
assertThat(cache.containsUri(firstUri)).isTrue();
|
||||
assertThat(cache.containsUri(secondUri)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getNullReturnsNull() {
|
||||
FullSegmentEncryptionKeyCache cache = new FullSegmentEncryptionKeyCache(/* maxSize= */ 5);
|
||||
cache.put(firstUri, encryptionKey);
|
||||
assertThat(cache.get(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putNullKeyThrowsException() {
|
||||
FullSegmentEncryptionKeyCache cache = new FullSegmentEncryptionKeyCache(/* maxSize= */ 5);
|
||||
try {
|
||||
cache.put(null, encryptionKey);
|
||||
fail();
|
||||
} catch (NullPointerException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putNullValueThrowsException() {
|
||||
FullSegmentEncryptionKeyCache cache = new FullSegmentEncryptionKeyCache(/* maxSize= */ 5);
|
||||
try {
|
||||
cache.put(firstUri, null);
|
||||
fail();
|
||||
} catch (NullPointerException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void containsNullThrowsException() {
|
||||
FullSegmentEncryptionKeyCache cache = new FullSegmentEncryptionKeyCache(/* maxSize= */ 5);
|
||||
try {
|
||||
cache.containsUri(null);
|
||||
fail();
|
||||
} catch (NullPointerException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeNullThrowsException() {
|
||||
FullSegmentEncryptionKeyCache cache = new FullSegmentEncryptionKeyCache(/* maxSize= */ 5);
|
||||
try {
|
||||
cache.remove(null);
|
||||
fail();
|
||||
} catch (NullPointerException expected) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oldestElementRemoved() {
|
||||
FullSegmentEncryptionKeyCache cache = new FullSegmentEncryptionKeyCache(/* maxSize= */ 2);
|
||||
|
||||
cache.put(firstUri, encryptionKey);
|
||||
cache.put(secondUri, new byte[] {1, 2, 3, 4});
|
||||
cache.put(Uri.parse("www.nest.com"), new byte[] {1, 2, 3, 4});
|
||||
|
||||
assertThat(cache.containsUri(firstUri)).isFalse();
|
||||
assertThat(cache.containsUri(secondUri)).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Elements need to be removed and reinserted, rather than just updated, to change their position
|
||||
* in the removal queue.
|
||||
*/
|
||||
@Test
|
||||
public void updatingElementDoesntChangeAgeForRemoval() {
|
||||
FullSegmentEncryptionKeyCache cache = new FullSegmentEncryptionKeyCache(/* maxSize= */ 2);
|
||||
|
||||
cache.put(firstUri, encryptionKey);
|
||||
cache.put(secondUri, new byte[] {1, 2, 3, 4});
|
||||
// Update firstUri element
|
||||
cache.put(firstUri, new byte[] {10, 11, 12, 12});
|
||||
cache.put(Uri.parse("www.nest.com"), new byte[] {1, 2, 3, 4});
|
||||
|
||||
// firstUri is still removed before secondUri, despite the update
|
||||
assertThat(cache.containsUri(firstUri)).isFalse();
|
||||
assertThat(cache.containsUri(secondUri)).isTrue();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user