mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
DataSourceContractTest
: Add expected response headers
PiperOrigin-RevId: 688556440
This commit is contained in:
parent
8260bb3d2e
commit
219565c15e
@ -43,12 +43,15 @@ import androidx.media3.datasource.DataSpec;
|
||||
import androidx.media3.datasource.TransferListener;
|
||||
import com.google.common.base.Ascii;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.errorprone.annotations.ForOverride;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
@ -569,6 +572,31 @@ public abstract class DataSourceContractTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResponseHeaders_returnsExpectedValues() throws Exception {
|
||||
forAllTestResourcesAndDataSources(
|
||||
(resource, dataSource) -> {
|
||||
try {
|
||||
dataSource.open(new DataSpec(resource.getUri()));
|
||||
// Iterate over the expected headers (instead of using Truth's batch
|
||||
// containsAtLeastEntriesIn() method) in order to leverage the case-insensitivity of
|
||||
// the DataSource.getResponseHeaders() implementation.
|
||||
Map<String, List<String>> actualHeaders = dataSource.getResponseHeaders();
|
||||
for (Map.Entry<String, List<String>> expectedHeaders :
|
||||
resource.getResponseHeaders().entrySet()) {
|
||||
assertWithMessage("Header values for key=%s", expectedHeaders.getKey())
|
||||
.that(actualHeaders.get(expectedHeaders.getKey()))
|
||||
.isEqualTo(expectedHeaders.getValue());
|
||||
}
|
||||
for (String unexpectedKey : resource.getUnexpectedResponseHeaderKeys()) {
|
||||
assertThat(actualHeaders).doesNotContainKey(unexpectedKey);
|
||||
}
|
||||
} finally {
|
||||
dataSource.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getResponseHeaders_noNullKeysOrValues() throws Exception {
|
||||
forAllTestResourcesAndDataSources(
|
||||
@ -741,12 +769,22 @@ public abstract class DataSourceContractTest {
|
||||
@Nullable private final String name;
|
||||
private final Uri uri;
|
||||
private final Uri resolvedUri;
|
||||
private final Map<String, List<String>> responseHeaders;
|
||||
private final Set<String> unexpectedResponseHeaderKeys;
|
||||
private final byte[] expectedBytes;
|
||||
|
||||
private TestResource(@Nullable String name, Uri uri, Uri resolvedUri, byte[] expectedBytes) {
|
||||
private TestResource(
|
||||
@Nullable String name,
|
||||
Uri uri,
|
||||
Uri resolvedUri,
|
||||
Map<String, List<String>> responseHeaders,
|
||||
Set<String> unexpectedResponseHeaderKeys,
|
||||
byte[] expectedBytes) {
|
||||
this.name = name;
|
||||
this.uri = uri;
|
||||
this.resolvedUri = resolvedUri;
|
||||
this.responseHeaders = responseHeaders;
|
||||
this.unexpectedResponseHeaderKeys = unexpectedResponseHeaderKeys;
|
||||
this.expectedBytes = expectedBytes;
|
||||
}
|
||||
|
||||
@ -769,6 +807,25 @@ public abstract class DataSourceContractTest {
|
||||
return resolvedUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the headers associated with this resource that are expected to be present in {@link
|
||||
* DataSource#getResponseHeaders()}.
|
||||
*
|
||||
* <p>This doesn't have to be an exhaustive list, extra headers in {@link
|
||||
* DataSource#getResponseHeaders()} are ignored.
|
||||
*/
|
||||
public Map<String, List<String>> getResponseHeaders() {
|
||||
return responseHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keys that must <b>not</b> be present in {@link DataSource#getResponseHeaders()}
|
||||
* when reading this resource.
|
||||
*/
|
||||
public Set<String> getUnexpectedResponseHeaderKeys() {
|
||||
return unexpectedResponseHeaderKeys;
|
||||
}
|
||||
|
||||
/** Returns the expected contents of this resource. */
|
||||
public byte[] getExpectedBytes() {
|
||||
return expectedBytes;
|
||||
@ -779,8 +836,15 @@ public abstract class DataSourceContractTest {
|
||||
private @MonotonicNonNull String name;
|
||||
private @MonotonicNonNull Uri uri;
|
||||
private @MonotonicNonNull Uri resolvedUri;
|
||||
private Map<String, List<String>> responseHeaders;
|
||||
private Set<String> unexpectedResponseHeaderKeys;
|
||||
private byte @MonotonicNonNull [] expectedBytes;
|
||||
|
||||
public Builder() {
|
||||
responseHeaders = ImmutableMap.of();
|
||||
unexpectedResponseHeaderKeys = ImmutableSet.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a human-readable name for this resource which will be shown in test failure messages.
|
||||
*/
|
||||
@ -822,6 +886,29 @@ public abstract class DataSourceContractTest {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the headers associated with this resource that are expected to be present in {@link
|
||||
* DataSource#getResponseHeaders()}.
|
||||
*
|
||||
* <p>This doesn't have to be an exhaustive list, extra headers in {@link
|
||||
* DataSource#getResponseHeaders()} are ignored. Defaults to an empty map.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setResponseHeaders(Map<String, List<String>> responseHeaders) {
|
||||
this.responseHeaders = responseHeaders;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the keys that must <b>not</b> be present in {@link DataSource#getResponseHeaders()}
|
||||
* when reading this resource. Defaults to an empty set.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setUnexpectedResponseHeaderKeys(Set<String> unexpectedResponseHeaderKeys) {
|
||||
this.unexpectedResponseHeaderKeys = unexpectedResponseHeaderKeys;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the expected contents of this resource.
|
||||
*
|
||||
@ -839,6 +926,8 @@ public abstract class DataSourceContractTest {
|
||||
name,
|
||||
checkNotNull(uri),
|
||||
resolvedUri != null ? resolvedUri : uri,
|
||||
ImmutableMap.copyOf(responseHeaders),
|
||||
ImmutableSet.copyOf(unexpectedResponseHeaderKeys),
|
||||
checkNotNull(expectedBytes));
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,12 @@ import android.net.Uri;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.datasource.HttpDataSource;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.net.HttpHeaders;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
@ -33,12 +38,19 @@ import org.junit.rules.ExternalResource;
|
||||
/** A JUnit {@link Rule} that creates test resources for {@link HttpDataSource} contract tests. */
|
||||
@UnstableApi
|
||||
public class HttpDataSourceTestEnv extends ExternalResource {
|
||||
|
||||
private static final ImmutableListMultimap<String, String> EXTRA_HEADERS =
|
||||
ImmutableListMultimap.<String, String>builder()
|
||||
.putAll("X-Test-Header", "test value1", "test value2")
|
||||
.build();
|
||||
|
||||
private static int seed = 0;
|
||||
private static final WebServerDispatcher.Resource RANGE_SUPPORTED =
|
||||
new WebServerDispatcher.Resource.Builder()
|
||||
.setPath("/supports/range-requests")
|
||||
.setData(TestUtil.buildTestData(/* length= */ 20, seed++))
|
||||
.supportsRangeRequests(true)
|
||||
.setExtraResponseHeaders(EXTRA_HEADERS)
|
||||
.build();
|
||||
|
||||
private static final WebServerDispatcher.Resource RANGE_SUPPORTED_LENGTH_UNKNOWN =
|
||||
@ -47,6 +59,7 @@ public class HttpDataSourceTestEnv extends ExternalResource {
|
||||
.setData(TestUtil.buildTestData(/* length= */ 20, seed++))
|
||||
.supportsRangeRequests(true)
|
||||
.resolvesToUnknownLength(true)
|
||||
.setExtraResponseHeaders(EXTRA_HEADERS)
|
||||
.build();
|
||||
|
||||
private static final WebServerDispatcher.Resource RANGE_NOT_SUPPORTED =
|
||||
@ -54,6 +67,7 @@ public class HttpDataSourceTestEnv extends ExternalResource {
|
||||
.setPath("/doesnt/support/range-requests")
|
||||
.setData(TestUtil.buildTestData(/* length= */ 20, seed++))
|
||||
.supportsRangeRequests(false)
|
||||
.setExtraResponseHeaders(EXTRA_HEADERS)
|
||||
.build();
|
||||
|
||||
private static final WebServerDispatcher.Resource RANGE_NOT_SUPPORTED_LENGTH_UNKNOWN =
|
||||
@ -62,6 +76,7 @@ public class HttpDataSourceTestEnv extends ExternalResource {
|
||||
.setData(TestUtil.buildTestData(/* length= */ 20, seed++))
|
||||
.supportsRangeRequests(false)
|
||||
.resolvesToUnknownLength(true)
|
||||
.setExtraResponseHeaders(EXTRA_HEADERS)
|
||||
.build();
|
||||
|
||||
private static final WebServerDispatcher.Resource GZIP_ENABLED =
|
||||
@ -69,6 +84,7 @@ public class HttpDataSourceTestEnv extends ExternalResource {
|
||||
.setPath("/gzip/enabled")
|
||||
.setData(TestUtil.buildTestData(/* length= */ 20, seed++))
|
||||
.setGzipSupport(WebServerDispatcher.Resource.GZIP_SUPPORT_ENABLED)
|
||||
.setExtraResponseHeaders(EXTRA_HEADERS)
|
||||
.build();
|
||||
|
||||
private static final WebServerDispatcher.Resource GZIP_FORCED =
|
||||
@ -76,6 +92,7 @@ public class HttpDataSourceTestEnv extends ExternalResource {
|
||||
.setPath("/gzip/forced")
|
||||
.setData(TestUtil.buildTestData(/* length= */ 20, seed++))
|
||||
.setGzipSupport(WebServerDispatcher.Resource.GZIP_SUPPORT_FORCED)
|
||||
.setExtraResponseHeaders(EXTRA_HEADERS)
|
||||
.build();
|
||||
|
||||
private static final WebServerDispatcher.Resource REDIRECTS_TO_RANGE_SUPPORTED =
|
||||
@ -147,10 +164,15 @@ public class HttpDataSourceTestEnv extends ExternalResource {
|
||||
|
||||
private DataSourceContractTest.TestResource createTestResource(
|
||||
String name, WebServerDispatcher.Resource resource) {
|
||||
return new DataSourceContractTest.TestResource.Builder()
|
||||
.setName(name)
|
||||
.setUri(Uri.parse(originServer.url(resource.getPath()).toString()))
|
||||
.setExpectedBytes(resource.getData())
|
||||
.build();
|
||||
DataSourceContractTest.TestResource.Builder testResource =
|
||||
new DataSourceContractTest.TestResource.Builder()
|
||||
.setName(name)
|
||||
.setUri(Uri.parse(originServer.url(resource.getPath()).toString()))
|
||||
.setResponseHeaders(Maps.transformValues(EXTRA_HEADERS.asMap(), v -> (List<String>) v))
|
||||
.setExpectedBytes(resource.getData());
|
||||
if (resource.resolvesToUnknownLength()) {
|
||||
testResource.setUnexpectedResponseHeaderKeys(ImmutableSet.of(HttpHeaders.CONTENT_LENGTH));
|
||||
}
|
||||
return testResource.build();
|
||||
}
|
||||
}
|
||||
|
@ -32,8 +32,10 @@ import androidx.media3.common.util.Util;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableListMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
@ -41,6 +43,7 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import okhttp3.mockwebserver.Dispatcher;
|
||||
@ -102,10 +105,12 @@ public class WebServerDispatcher extends Dispatcher {
|
||||
private boolean supportsRangeRequests;
|
||||
private boolean resolvesToUnknownLength;
|
||||
private @GzipSupport int gzipSupport;
|
||||
private ImmutableListMultimap<String, String> extraResponseHeaders;
|
||||
|
||||
/** Constructs an instance. */
|
||||
public Builder() {
|
||||
this.gzipSupport = GZIP_SUPPORT_DISABLED;
|
||||
this.extraResponseHeaders = ImmutableListMultimap.of();
|
||||
}
|
||||
|
||||
private Builder(Resource resource) {
|
||||
@ -114,6 +119,7 @@ public class WebServerDispatcher extends Dispatcher {
|
||||
this.supportsRangeRequests = resource.supportsRangeRequests();
|
||||
this.resolvesToUnknownLength = resource.resolvesToUnknownLength();
|
||||
this.gzipSupport = resource.getGzipSupport();
|
||||
this.extraResponseHeaders = resource.getExtraResponseHeaders();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,6 +181,17 @@ public class WebServerDispatcher extends Dispatcher {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the extra response headers that should be attached.
|
||||
*
|
||||
* @return this builder, for convenience.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setExtraResponseHeaders(Multimap<String, String> extraResponseHeaders) {
|
||||
this.extraResponseHeaders = ImmutableListMultimap.copyOf(extraResponseHeaders);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds the {@link Resource}. */
|
||||
public Resource build() {
|
||||
if (gzipSupport != GZIP_SUPPORT_DISABLED) {
|
||||
@ -186,7 +203,8 @@ public class WebServerDispatcher extends Dispatcher {
|
||||
checkNotNull(data),
|
||||
supportsRangeRequests,
|
||||
resolvesToUnknownLength,
|
||||
gzipSupport);
|
||||
gzipSupport,
|
||||
extraResponseHeaders);
|
||||
}
|
||||
}
|
||||
|
||||
@ -195,18 +213,21 @@ public class WebServerDispatcher extends Dispatcher {
|
||||
private final boolean supportsRangeRequests;
|
||||
private final boolean resolvesToUnknownLength;
|
||||
private final @GzipSupport int gzipSupport;
|
||||
ImmutableListMultimap<String, String> extraResponseHeaders;
|
||||
|
||||
private Resource(
|
||||
String path,
|
||||
byte[] data,
|
||||
boolean supportsRangeRequests,
|
||||
boolean resolvesToUnknownLength,
|
||||
@GzipSupport int gzipSupport) {
|
||||
@GzipSupport int gzipSupport,
|
||||
ImmutableListMultimap<String, String> extraResponseHeaders) {
|
||||
this.path = path;
|
||||
this.data = data;
|
||||
this.supportsRangeRequests = supportsRangeRequests;
|
||||
this.resolvesToUnknownLength = resolvesToUnknownLength;
|
||||
this.gzipSupport = gzipSupport;
|
||||
this.extraResponseHeaders = extraResponseHeaders;
|
||||
}
|
||||
|
||||
/** Returns the path this resource is available at. */
|
||||
@ -234,6 +255,11 @@ public class WebServerDispatcher extends Dispatcher {
|
||||
return gzipSupport;
|
||||
}
|
||||
|
||||
/** Returns the extra response headers that should be attached. */
|
||||
public ImmutableListMultimap<String, String> getExtraResponseHeaders() {
|
||||
return extraResponseHeaders;
|
||||
}
|
||||
|
||||
/** Returns a new {@link Builder} initialized with the values from this instance. */
|
||||
public Builder buildUpon() {
|
||||
return new Builder(this);
|
||||
@ -270,6 +296,9 @@ public class WebServerDispatcher extends Dispatcher {
|
||||
return response.setResponseCode(404);
|
||||
}
|
||||
Resource resource = checkNotNull(resourcesByPath.get(requestPath));
|
||||
for (Map.Entry<String, String> extraHeader : resource.getExtraResponseHeaders().entries()) {
|
||||
response.addHeader(extraHeader.getKey(), extraHeader.getValue());
|
||||
}
|
||||
byte[] resourceData = resource.getData();
|
||||
if (resource.supportsRangeRequests()) {
|
||||
response.setHeader("Accept-Ranges", "bytes");
|
||||
|
Loading…
x
Reference in New Issue
Block a user