mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
DataSourceContractTest
: Tighten assertions around 'not found' URIs
This change: 1. Updates `DataSourceContractTest` to allow multiple "not found" resources, and to include additional info (e.g. headers) on them. 2. Updates the contract test to assert that `DataSource.getUri()` returns the expected (non-null) value for "not found" resources between the failed `open()` call and a subsequent `close()` call. The `DataSource` is 'open' at this point (since it needs to be 'closed' later), so `getUri()` must return non-null. * This change also fixes some implementations to comply with this contract. It also renames some imprecisely named `opened` booleans that **don't** track whether the `DataSource` is open or not. 3. Updates the contract test assertions to enforce that `DataSource.getResponseHeaders()` returns any headers associated with the 'not found' resource. 4. Configures `HttpDataSourceTestEnv` to provide both 404 and "server not found" resources, with the former having expected headers associated with it. PiperOrigin-RevId: 689316121
This commit is contained in:
parent
d25a423888
commit
4a406be1bf
@ -24,6 +24,14 @@
|
|||||||
resolved URI (as documented). Where this is different to the requested
|
resolved URI (as documented). Where this is different to the requested
|
||||||
URI, tests can indicate this using the new
|
URI, tests can indicate this using the new
|
||||||
`DataSourceContractTest.TestResource.Builder.setResolvedUri()` method.
|
`DataSourceContractTest.TestResource.Builder.setResolvedUri()` method.
|
||||||
|
* `DataSourceContractTest`: Assert that `DataSource.getUri()` and
|
||||||
|
`getResponseHeaders()` return their 'open' value after a failed call to
|
||||||
|
`open()` (due to a 'not found' resource) and before a subsequent
|
||||||
|
`close()` call.
|
||||||
|
* Overriding `DataSourceContractTest.getNotFoundResources()` allows
|
||||||
|
test sub-classes to provide multiple 'not found' resources, and to
|
||||||
|
provide any expected headers too. This allows to distinguish between
|
||||||
|
HTTP 404 (with headers) and "server not found" (no headers).
|
||||||
* Audio:
|
* Audio:
|
||||||
* Video:
|
* Video:
|
||||||
* Text:
|
* Text:
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.datasource;
|
package androidx.media3.datasource;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.media3.test.utils.DataSourceContractTest;
|
import androidx.media3.test.utils.DataSourceContractTest;
|
||||||
import androidx.media3.test.utils.HttpDataSourceTestEnv;
|
import androidx.media3.test.utils.HttpDataSourceTestEnv;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.List;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ public class DefaultHttpDataSourceContractTest extends DataSourceContractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Uri getNotFoundUri() {
|
protected List<TestResource> getNotFoundResources() {
|
||||||
return Uri.parse(httpDataSourceTestEnv.getNonexistentUrl());
|
return httpDataSourceTestEnv.getNotFoundResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.datasource;
|
package androidx.media3.datasource;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.net.http.HttpEngine;
|
import android.net.http.HttpEngine;
|
||||||
import androidx.media3.test.utils.DataSourceContractTest;
|
import androidx.media3.test.utils.DataSourceContractTest;
|
||||||
import androidx.media3.test.utils.HttpDataSourceTestEnv;
|
import androidx.media3.test.utils.HttpDataSourceTestEnv;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
@ -53,7 +53,7 @@ public class HttpEngineDataSourceContractTest extends DataSourceContractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Uri getNotFoundUri() {
|
protected List<TestResource> getNotFoundResources() {
|
||||||
return Uri.parse(httpDataSourceTestEnv.getNonexistentUrl());
|
return httpDataSourceTestEnv.getNotFoundResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
@Nullable private DataSpec dataSpec;
|
@Nullable private DataSpec dataSpec;
|
||||||
@Nullable private HttpURLConnection connection;
|
@Nullable private HttpURLConnection connection;
|
||||||
@Nullable private InputStream inputStream;
|
@Nullable private InputStream inputStream;
|
||||||
private boolean opened;
|
private boolean transferStarted;
|
||||||
private int responseCode;
|
private int responseCode;
|
||||||
private long bytesToRead;
|
private long bytesToRead;
|
||||||
private long bytesRead;
|
private long bytesRead;
|
||||||
@ -296,7 +296,13 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return connection == null ? null : Uri.parse(connection.getURL().toString());
|
if (connection != null) {
|
||||||
|
return Uri.parse(connection.getURL().toString());
|
||||||
|
} else if (dataSpec != null) {
|
||||||
|
return dataSpec.uri;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@ -372,7 +378,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
long documentSize =
|
long documentSize =
|
||||||
HttpUtil.getDocumentSize(connection.getHeaderField(HttpHeaders.CONTENT_RANGE));
|
HttpUtil.getDocumentSize(connection.getHeaderField(HttpHeaders.CONTENT_RANGE));
|
||||||
if (dataSpec.position == documentSize) {
|
if (dataSpec.position == documentSize) {
|
||||||
opened = true;
|
transferStarted = true;
|
||||||
transferStarted(dataSpec);
|
transferStarted(dataSpec);
|
||||||
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
|
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
|
||||||
}
|
}
|
||||||
@ -442,7 +448,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
HttpDataSourceException.TYPE_OPEN);
|
HttpDataSourceException.TYPE_OPEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
transferStarted = true;
|
||||||
transferStarted(dataSpec);
|
transferStarted(dataSpec);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -493,10 +499,12 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
} finally {
|
} finally {
|
||||||
inputStream = null;
|
inputStream = null;
|
||||||
closeConnectionQuietly();
|
closeConnectionQuietly();
|
||||||
if (opened) {
|
if (transferStarted) {
|
||||||
opened = false;
|
transferStarted = false;
|
||||||
transferEnded();
|
transferEnded();
|
||||||
}
|
}
|
||||||
|
connection = null;
|
||||||
|
dataSpec = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -787,7 +795,6 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Unexpected error while disconnecting", e);
|
Log.e(TAG, "Unexpected error while disconnecting", e);
|
||||||
}
|
}
|
||||||
connection = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,9 +38,6 @@ import androidx.media3.common.util.Clock;
|
|||||||
import androidx.media3.common.util.ConditionVariable;
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.HttpDataSource.CleartextNotPermittedException;
|
|
||||||
import androidx.media3.datasource.HttpDataSource.HttpDataSourceException;
|
|
||||||
import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException;
|
|
||||||
import com.google.common.base.Ascii;
|
import com.google.common.base.Ascii;
|
||||||
import com.google.common.base.Predicate;
|
import com.google.common.base.Predicate;
|
||||||
import com.google.common.net.HttpHeaders;
|
import com.google.common.net.HttpHeaders;
|
||||||
@ -341,7 +338,7 @@ public final class HttpEngineDataSource extends BaseDataSource implements HttpDa
|
|||||||
private final boolean keepPostFor302Redirects;
|
private final boolean keepPostFor302Redirects;
|
||||||
|
|
||||||
// Accessed by the calling thread only.
|
// Accessed by the calling thread only.
|
||||||
private boolean opened;
|
private boolean transferStarted;
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
|
|
||||||
@Nullable private DataSpec currentDataSpec;
|
@Nullable private DataSpec currentDataSpec;
|
||||||
@ -430,14 +427,20 @@ public final class HttpEngineDataSource extends BaseDataSource implements HttpDa
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return responseInfo == null ? null : Uri.parse(responseInfo.getUrl());
|
if (responseInfo != null) {
|
||||||
|
return Uri.parse(responseInfo.getUrl());
|
||||||
|
} else if (currentDataSpec != null) {
|
||||||
|
return currentDataSpec.uri;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
||||||
Assertions.checkNotNull(dataSpec);
|
Assertions.checkNotNull(dataSpec);
|
||||||
Assertions.checkState(!opened);
|
Assertions.checkState(!transferStarted);
|
||||||
|
|
||||||
operation.close();
|
operation.close();
|
||||||
resetConnectTimeout();
|
resetConnectTimeout();
|
||||||
@ -499,7 +502,7 @@ public final class HttpEngineDataSource extends BaseDataSource implements HttpDa
|
|||||||
long documentSize =
|
long documentSize =
|
||||||
HttpUtil.getDocumentSize(getFirstHeader(responseHeaders, HttpHeaders.CONTENT_RANGE));
|
HttpUtil.getDocumentSize(getFirstHeader(responseHeaders, HttpHeaders.CONTENT_RANGE));
|
||||||
if (dataSpec.position == documentSize) {
|
if (dataSpec.position == documentSize) {
|
||||||
opened = true;
|
transferStarted = true;
|
||||||
transferStarted(dataSpec);
|
transferStarted(dataSpec);
|
||||||
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
|
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
|
||||||
}
|
}
|
||||||
@ -558,7 +561,7 @@ public final class HttpEngineDataSource extends BaseDataSource implements HttpDa
|
|||||||
bytesRemaining = dataSpec.length;
|
bytesRemaining = dataSpec.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
transferStarted = true;
|
||||||
transferStarted(dataSpec);
|
transferStarted(dataSpec);
|
||||||
|
|
||||||
skipFully(bytesToSkip, dataSpec);
|
skipFully(bytesToSkip, dataSpec);
|
||||||
@ -568,7 +571,7 @@ public final class HttpEngineDataSource extends BaseDataSource implements HttpDa
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
||||||
Assertions.checkState(opened);
|
Assertions.checkState(transferStarted);
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -639,7 +642,7 @@ public final class HttpEngineDataSource extends BaseDataSource implements HttpDa
|
|||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public int read(ByteBuffer buffer) throws HttpDataSourceException {
|
public int read(ByteBuffer buffer) throws HttpDataSourceException {
|
||||||
Assertions.checkState(opened);
|
Assertions.checkState(transferStarted);
|
||||||
|
|
||||||
if (!buffer.isDirect()) {
|
if (!buffer.isDirect()) {
|
||||||
throw new IllegalArgumentException("Passed buffer is not a direct ByteBuffer");
|
throw new IllegalArgumentException("Passed buffer is not a direct ByteBuffer");
|
||||||
@ -696,8 +699,8 @@ public final class HttpEngineDataSource extends BaseDataSource implements HttpDa
|
|||||||
responseInfo = null;
|
responseInfo = null;
|
||||||
exception = null;
|
exception = null;
|
||||||
finished = false;
|
finished = false;
|
||||||
if (opened) {
|
if (transferStarted) {
|
||||||
opened = false;
|
transferStarted = false;
|
||||||
transferEnded();
|
transferEnded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.datasource.cronet;
|
package androidx.media3.datasource.cronet;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.media3.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.test.utils.DataSourceContractTest;
|
import androidx.media3.test.utils.DataSourceContractTest;
|
||||||
import androidx.media3.test.utils.HttpDataSourceTestEnv;
|
import androidx.media3.test.utils.HttpDataSourceTestEnv;
|
||||||
@ -66,7 +65,7 @@ public class CronetDataSourceContractTest extends DataSourceContractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Uri getNotFoundUri() {
|
protected List<TestResource> getNotFoundResources() {
|
||||||
return Uri.parse(httpDataSourceTestEnv.getNonexistentUrl());
|
return httpDataSourceTestEnv.getNotFoundResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -463,7 +463,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
private final boolean keepPostFor302Redirects;
|
private final boolean keepPostFor302Redirects;
|
||||||
|
|
||||||
// Accessed by the calling thread only.
|
// Accessed by the calling thread only.
|
||||||
private boolean opened;
|
private boolean transferStarted;
|
||||||
private long bytesRemaining;
|
private long bytesRemaining;
|
||||||
|
|
||||||
// Written from the calling thread only. currentUrlRequest.start() calls ensure writes are visible
|
// Written from the calling thread only. currentUrlRequest.start() calls ensure writes are visible
|
||||||
@ -555,14 +555,20 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return responseInfo == null ? null : Uri.parse(responseInfo.getUrl());
|
if (responseInfo != null) {
|
||||||
|
return Uri.parse(responseInfo.getUrl());
|
||||||
|
} else if (currentDataSpec != null) {
|
||||||
|
return currentDataSpec.uri;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
||||||
Assertions.checkNotNull(dataSpec);
|
Assertions.checkNotNull(dataSpec);
|
||||||
Assertions.checkState(!opened);
|
Assertions.checkState(!transferStarted);
|
||||||
|
|
||||||
operation.close();
|
operation.close();
|
||||||
resetConnectTimeout();
|
resetConnectTimeout();
|
||||||
@ -624,7 +630,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
long documentSize =
|
long documentSize =
|
||||||
HttpUtil.getDocumentSize(getFirstHeader(responseHeaders, HttpHeaders.CONTENT_RANGE));
|
HttpUtil.getDocumentSize(getFirstHeader(responseHeaders, HttpHeaders.CONTENT_RANGE));
|
||||||
if (dataSpec.position == documentSize) {
|
if (dataSpec.position == documentSize) {
|
||||||
opened = true;
|
transferStarted = true;
|
||||||
transferStarted(dataSpec);
|
transferStarted(dataSpec);
|
||||||
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
|
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
|
||||||
}
|
}
|
||||||
@ -683,7 +689,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
bytesRemaining = dataSpec.length;
|
bytesRemaining = dataSpec.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
transferStarted = true;
|
||||||
transferStarted(dataSpec);
|
transferStarted(dataSpec);
|
||||||
|
|
||||||
skipFully(bytesToSkip, dataSpec);
|
skipFully(bytesToSkip, dataSpec);
|
||||||
@ -693,7 +699,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
||||||
Assertions.checkState(opened);
|
Assertions.checkState(transferStarted);
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -764,7 +770,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public int read(ByteBuffer buffer) throws HttpDataSourceException {
|
public int read(ByteBuffer buffer) throws HttpDataSourceException {
|
||||||
Assertions.checkState(opened);
|
Assertions.checkState(transferStarted);
|
||||||
|
|
||||||
if (!buffer.isDirect()) {
|
if (!buffer.isDirect()) {
|
||||||
throw new IllegalArgumentException("Passed buffer is not a direct ByteBuffer");
|
throw new IllegalArgumentException("Passed buffer is not a direct ByteBuffer");
|
||||||
@ -818,8 +824,8 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
responseInfo = null;
|
responseInfo = null;
|
||||||
exception = null;
|
exception = null;
|
||||||
finished = false;
|
finished = false;
|
||||||
if (opened) {
|
if (transferStarted) {
|
||||||
opened = false;
|
transferStarted = false;
|
||||||
transferEnded();
|
transferEnded();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,7 +192,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
@Nullable private DataSpec dataSpec;
|
@Nullable private DataSpec dataSpec;
|
||||||
@Nullable private Response response;
|
@Nullable private Response response;
|
||||||
@Nullable private InputStream responseByteStream;
|
@Nullable private InputStream responseByteStream;
|
||||||
private boolean opened;
|
private boolean connectionEstablished;
|
||||||
private long bytesToRead;
|
private long bytesToRead;
|
||||||
private long bytesRead;
|
private long bytesRead;
|
||||||
|
|
||||||
@ -215,7 +215,13 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return response == null ? null : Uri.parse(response.request().url().toString());
|
if (response != null) {
|
||||||
|
return Uri.parse(response.request().url().toString());
|
||||||
|
} else if (dataSpec != null) {
|
||||||
|
return dataSpec.uri;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
@ -281,7 +287,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
long documentSize =
|
long documentSize =
|
||||||
HttpUtil.getDocumentSize(response.headers().get(HttpHeaders.CONTENT_RANGE));
|
HttpUtil.getDocumentSize(response.headers().get(HttpHeaders.CONTENT_RANGE));
|
||||||
if (dataSpec.position == documentSize) {
|
if (dataSpec.position == documentSize) {
|
||||||
opened = true;
|
connectionEstablished = true;
|
||||||
transferStarted(dataSpec);
|
transferStarted(dataSpec);
|
||||||
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
|
return dataSpec.length != C.LENGTH_UNSET ? dataSpec.length : 0;
|
||||||
}
|
}
|
||||||
@ -325,7 +331,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
bytesToRead = contentLength != -1 ? (contentLength - bytesToSkip) : C.LENGTH_UNSET;
|
bytesToRead = contentLength != -1 ? (contentLength - bytesToSkip) : C.LENGTH_UNSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
opened = true;
|
connectionEstablished = true;
|
||||||
transferStarted(dataSpec);
|
transferStarted(dataSpec);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -352,11 +358,13 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (opened) {
|
if (connectionEstablished) {
|
||||||
opened = false;
|
connectionEstablished = false;
|
||||||
transferEnded();
|
transferEnded();
|
||||||
closeConnectionQuietly();
|
closeConnectionQuietly();
|
||||||
}
|
}
|
||||||
|
response = null;
|
||||||
|
dataSpec = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Establishes a connection. */
|
/** Establishes a connection. */
|
||||||
@ -524,7 +532,6 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
private void closeConnectionQuietly() {
|
private void closeConnectionQuietly() {
|
||||||
if (response != null) {
|
if (response != null) {
|
||||||
Assertions.checkNotNull(response.body()).close();
|
Assertions.checkNotNull(response.body()).close();
|
||||||
response = null;
|
|
||||||
}
|
}
|
||||||
responseByteStream = null;
|
responseByteStream = null;
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.datasource.okhttp;
|
package androidx.media3.datasource.okhttp;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import androidx.media3.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.test.utils.DataSourceContractTest;
|
import androidx.media3.test.utils.DataSourceContractTest;
|
||||||
import androidx.media3.test.utils.HttpDataSourceTestEnv;
|
import androidx.media3.test.utils.HttpDataSourceTestEnv;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.List;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@ -42,7 +42,7 @@ public class OkHttpDataSourceContractTest extends DataSourceContractTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Uri getNotFoundUri() {
|
protected List<TestResource> getNotFoundResources() {
|
||||||
return Uri.parse(httpDataSourceTestEnv.getNonexistentUrl());
|
return httpDataSourceTestEnv.getNotFoundResources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.test.utils;
|
package androidx.media3.test.utils;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Util.castNonNull;
|
import static androidx.media3.common.util.Util.castNonNull;
|
||||||
@ -86,7 +85,8 @@ public abstract class DataSourceContractTest {
|
|||||||
*/
|
*/
|
||||||
@ForOverride
|
@ForOverride
|
||||||
protected DataSource createDataSource() throws Exception {
|
protected DataSource createDataSource() throws Exception {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException(
|
||||||
|
"Either createDataSource or createDataSources must be implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,7 +97,8 @@ public abstract class DataSourceContractTest {
|
|||||||
*/
|
*/
|
||||||
@ForOverride
|
@ForOverride
|
||||||
protected List<DataSource> createDataSources() throws Exception {
|
protected List<DataSource> createDataSources() throws Exception {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException(
|
||||||
|
"Either createDataSource or createDataSources must be implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -124,7 +125,8 @@ public abstract class DataSourceContractTest {
|
|||||||
* Returns {@link TestResource} instances.
|
* Returns {@link TestResource} instances.
|
||||||
*
|
*
|
||||||
* <p>Each resource will be used to exercise the {@link DataSource} instance, allowing different
|
* <p>Each resource will be used to exercise the {@link DataSource} instance, allowing different
|
||||||
* behaviours to be tested.
|
* behaviours to be tested. Every {@link TestResource#getExpectedBytes()} must be at least 5
|
||||||
|
* bytes.
|
||||||
*
|
*
|
||||||
* <p>If multiple resources are returned, it's recommended to disambiguate them using {@link
|
* <p>If multiple resources are returned, it's recommended to disambiguate them using {@link
|
||||||
* TestResource.Builder#setName(String)}.
|
* TestResource.Builder#setName(String)}.
|
||||||
@ -136,9 +138,34 @@ public abstract class DataSourceContractTest {
|
|||||||
* Returns a {@link Uri} that doesn't resolve.
|
* Returns a {@link Uri} that doesn't resolve.
|
||||||
*
|
*
|
||||||
* <p>This is used to test how a {@link DataSource} handles nonexistent data.
|
* <p>This is used to test how a {@link DataSource} handles nonexistent data.
|
||||||
|
*
|
||||||
|
* <p>Only one of {@link #getNotFoundUri()} and {@link #getNotFoundResources()} should be
|
||||||
|
* implemented.
|
||||||
*/
|
*/
|
||||||
@ForOverride
|
@ForOverride
|
||||||
protected abstract Uri getNotFoundUri();
|
protected Uri getNotFoundUri() {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Either getNotFoundUri or getNotFoundUris must be implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a non-empty list of {@link TestResource} that don't resolve.
|
||||||
|
*
|
||||||
|
* <p>This is used to test how a {@link DataSource} handles nonexistent data. Multiple entries and
|
||||||
|
* the rest of the {@link TestResource} fields can be helpful for situations where the data can be
|
||||||
|
* "not found" for different reasons. For example in HTTP, 'server not found' generally results in
|
||||||
|
* a failed HTTP connection while 'file not found' generally results in a successful connection
|
||||||
|
* with a 404 HTTP error code and some response headers, and the handling code for these two cases
|
||||||
|
* may be different (and therefore worth testing separately).
|
||||||
|
*
|
||||||
|
* <p>Only one of {@link #getNotFoundUri()} and {@link #getNotFoundResources()} should be
|
||||||
|
* implemented.
|
||||||
|
*/
|
||||||
|
@ForOverride
|
||||||
|
protected List<TestResource> getNotFoundResources() {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Either getNotFoundUri or getNotFoundUris must be implemented.");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void unboundedDataSpec_readUntilEnd() throws Exception {
|
public void unboundedDataSpec_readUntilEnd() throws Exception {
|
||||||
@ -457,9 +484,10 @@ public abstract class DataSourceContractTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resourceNotFound() throws Exception {
|
public void resourceNotFound() throws Exception {
|
||||||
forAllDataSources(
|
forAllDataSourcesAndNotFoundResources(
|
||||||
dataSource -> {
|
(resource, dataSource) -> {
|
||||||
assertThrows(IOException.class, () -> dataSource.open(new DataSpec(getNotFoundUri())));
|
assertThrows(IOException.class, () -> dataSource.open(new DataSpec(resource.uri)));
|
||||||
|
|
||||||
dataSource.close();
|
dataSource.close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -522,8 +550,8 @@ public abstract class DataSourceContractTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void resourceNotFound_transferListenerCallbacks() throws Exception {
|
public void resourceNotFound_transferListenerCallbacks() throws Exception {
|
||||||
forAllDataSources(
|
forAllDataSourcesAndNotFoundResources(
|
||||||
dataSource -> {
|
(resource, dataSource) -> {
|
||||||
TransferListener listener = mock(TransferListener.class);
|
TransferListener listener = mock(TransferListener.class);
|
||||||
dataSource.addTransferListener(listener);
|
dataSource.addTransferListener(listener);
|
||||||
@Nullable DataSource callbackSource = getTransferListenerDataSource();
|
@Nullable DataSource callbackSource = getTransferListenerDataSource();
|
||||||
@ -531,7 +559,7 @@ public abstract class DataSourceContractTest {
|
|||||||
callbackSource = dataSource;
|
callbackSource = dataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThrows(IOException.class, () -> dataSource.open(new DataSpec(getNotFoundUri())));
|
assertThrows(IOException.class, () -> dataSource.open(new DataSpec(resource.uri)));
|
||||||
|
|
||||||
// Verify onTransferInitializing() has been called exactly from DataSource.open().
|
// Verify onTransferInitializing() has been called exactly from DataSource.open().
|
||||||
verify(listener).onTransferInitializing(eq(callbackSource), any(), anyBoolean());
|
verify(listener).onTransferInitializing(eq(callbackSource), any(), anyBoolean());
|
||||||
@ -561,13 +589,12 @@ public abstract class DataSourceContractTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getUri_resourceNotFound_returnsNullIfNotOpened() throws Exception {
|
public void getUri_resourceNotFound_returnsNullIfNotOpened() throws Exception {
|
||||||
forAllDataSources(
|
forAllDataSourcesAndNotFoundResources(
|
||||||
dataSource -> {
|
(resource, dataSource) -> {
|
||||||
assertThat(dataSource.getUri()).isNull();
|
assertThat(dataSource.getUri()).isNull();
|
||||||
|
assertThrows(IOException.class, () -> dataSource.open(new DataSpec(resource.uri)));
|
||||||
assertThrows(IOException.class, () -> dataSource.open(new DataSpec(getNotFoundUri())));
|
assertThat(dataSource.getUri()).isEqualTo(resource.uri);
|
||||||
dataSource.close();
|
dataSource.close();
|
||||||
|
|
||||||
assertThat(dataSource.getUri()).isNull();
|
assertThat(dataSource.getUri()).isNull();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -653,11 +680,23 @@ public abstract class DataSourceContractTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getResponseHeaders_resourceNotFound_isEmptyWhileNotOpen() throws Exception {
|
public void getResponseHeaders_resourceNotFound_isEmptyWhileNotOpen() throws Exception {
|
||||||
forAllDataSources(
|
forAllDataSourcesAndNotFoundResources(
|
||||||
dataSource -> {
|
(resource, dataSource) -> {
|
||||||
assertThat(dataSource.getResponseHeaders()).isEmpty();
|
assertThat(dataSource.getResponseHeaders()).isEmpty();
|
||||||
|
|
||||||
assertThrows(IOException.class, () -> dataSource.open(new DataSpec(getNotFoundUri())));
|
assertThrows(IOException.class, () -> dataSource.open(new DataSpec(resource.uri)));
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
dataSource.close();
|
dataSource.close();
|
||||||
|
|
||||||
assertThat(dataSource.getResponseHeaders()).isEmpty();
|
assertThat(dataSource.getResponseHeaders()).isEmpty();
|
||||||
@ -668,15 +707,14 @@ public abstract class DataSourceContractTest {
|
|||||||
void run(TestResource resource, DataSource dataSource) throws Exception;
|
void run(TestResource resource, DataSource dataSource) throws Exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
private interface DataSourceTest {
|
|
||||||
void run(DataSource dataSource) throws Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void forAllTestResourcesAndDataSources(TestResourceAndDataSourceTest test)
|
private void forAllTestResourcesAndDataSources(TestResourceAndDataSourceTest test)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
ImmutableList<TestResource> resources = getTestResources();
|
ImmutableList<TestResource> resources = getTestResources();
|
||||||
Assertions.checkArgument(!resources.isEmpty(), "Must provide at least one test resource.");
|
Assertions.checkArgument(!resources.isEmpty(), "Must provide at least one test resource.");
|
||||||
for (int i = 0; i < resources.size(); i++) {
|
for (int i = 0; i < resources.size(); i++) {
|
||||||
|
checkState(
|
||||||
|
resources.get(i).expectedBytes.length >= 5,
|
||||||
|
"TestResource.expectedBytes must be at least 5 bytes");
|
||||||
List<DataSource> dataSources = createDataSourcesInternal();
|
List<DataSource> dataSources = createDataSourcesInternal();
|
||||||
for (int j = 0; j < dataSources.size(); j++) {
|
for (int j = 0; j < dataSources.size(); j++) {
|
||||||
additionalFailureInfo.setInfo(getFailureLabel(resources, i, dataSources, j));
|
additionalFailureInfo.setInfo(getFailureLabel(resources, i, dataSources, j));
|
||||||
@ -686,19 +724,24 @@ public abstract class DataSourceContractTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void forAllDataSources(DataSourceTest test) throws Exception {
|
private void forAllDataSourcesAndNotFoundResources(TestResourceAndDataSourceTest test)
|
||||||
|
throws Exception {
|
||||||
|
List<TestResource> notFoundResources = getNotFoundResourcesInternal();
|
||||||
|
for (int i = 0; i < notFoundResources.size(); i++) {
|
||||||
List<DataSource> dataSources = createDataSourcesInternal();
|
List<DataSource> dataSources = createDataSourcesInternal();
|
||||||
for (int i = 0; i < dataSources.size(); i++) {
|
for (int j = 0; j < dataSources.size(); j++) {
|
||||||
additionalFailureInfo.setInfo(getDataSourceLabel(dataSources, i));
|
additionalFailureInfo.setInfo(
|
||||||
test.run(dataSources.get(i));
|
getNotFoundResourceLabel(notFoundResources, i, dataSources, j));
|
||||||
|
test.run(notFoundResources.get(i), dataSources.get(j));
|
||||||
additionalFailureInfo.setInfo(null);
|
additionalFailureInfo.setInfo(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<DataSource> createDataSourcesInternal() throws Exception {
|
private List<DataSource> createDataSourcesInternal() throws Exception {
|
||||||
try {
|
try {
|
||||||
List<DataSource> dataSources = createDataSources();
|
List<DataSource> dataSources = createDataSources();
|
||||||
checkState(!dataSources.isEmpty(), "Must provide at least on DataSource");
|
checkState(!dataSources.isEmpty(), "Must provide at least one DataSource");
|
||||||
assertThrows(UnsupportedOperationException.class, this::createDataSource);
|
assertThrows(UnsupportedOperationException.class, this::createDataSource);
|
||||||
return dataSources;
|
return dataSources;
|
||||||
} catch (UnsupportedOperationException e) {
|
} catch (UnsupportedOperationException e) {
|
||||||
@ -707,13 +750,50 @@ public abstract class DataSourceContractTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<TestResource> getNotFoundResourcesInternal() {
|
||||||
|
try {
|
||||||
|
List<TestResource> notFoundResources = getNotFoundResources();
|
||||||
|
checkState(!notFoundResources.isEmpty(), "Must provide at least one 'not found' resource");
|
||||||
|
assertThrows(UnsupportedOperationException.class, this::getNotFoundUri);
|
||||||
|
return notFoundResources;
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
// Expected if createDataSources is not implemented.
|
||||||
|
return ImmutableList.of(
|
||||||
|
new TestResource.Builder()
|
||||||
|
.setUri(getNotFoundUri())
|
||||||
|
.setExpectedBytes(Util.EMPTY_BYTE_ARRAY)
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a label to make it clear which not-found resource and data source caused a given test
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
private static String getNotFoundResourceLabel(
|
||||||
|
List<TestResource> resources,
|
||||||
|
int resourceIndex,
|
||||||
|
List<DataSource> dataSources,
|
||||||
|
int dataSourceIndex) {
|
||||||
|
return getFailureLabel("not-found", resources, resourceIndex, dataSources, dataSourceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
/** Build a label to make it clear which resource and data source caused a given test failure. */
|
/** Build a label to make it clear which resource and data source caused a given test failure. */
|
||||||
private static String getFailureLabel(
|
private static String getFailureLabel(
|
||||||
List<TestResource> resources,
|
List<TestResource> resources,
|
||||||
int resourceIndex,
|
int resourceIndex,
|
||||||
List<DataSource> dataSources,
|
List<DataSource> dataSources,
|
||||||
int dataSourceIndex) {
|
int dataSourceIndex) {
|
||||||
String resourceLabel = getResourceLabel(resources, resourceIndex);
|
return getFailureLabel("resources", resources, resourceIndex, dataSources, dataSourceIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getFailureLabel(
|
||||||
|
String resourcesType,
|
||||||
|
List<TestResource> resources,
|
||||||
|
int resourceIndex,
|
||||||
|
List<DataSource> dataSources,
|
||||||
|
int dataSourceIndex) {
|
||||||
|
String resourceLabel = getResourceLabel(resourcesType, resources, resourceIndex);
|
||||||
String dataSourceLabel = getDataSourceLabel(dataSources, dataSourceIndex);
|
String dataSourceLabel = getDataSourceLabel(dataSources, dataSourceIndex);
|
||||||
if (resourceLabel.isEmpty()) {
|
if (resourceLabel.isEmpty()) {
|
||||||
return dataSourceLabel;
|
return dataSourceLabel;
|
||||||
@ -724,13 +804,14 @@ public abstract class DataSourceContractTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getResourceLabel(List<TestResource> resources, int resourceIndex) {
|
private static String getResourceLabel(
|
||||||
|
String resourcesType, List<TestResource> resources, int resourceIndex) {
|
||||||
if (resources.size() == 1) {
|
if (resources.size() == 1) {
|
||||||
return "";
|
return "";
|
||||||
} else if (resources.get(resourceIndex).getName() != null) {
|
} else if (resources.get(resourceIndex).getName() != null) {
|
||||||
return "resource name: " + resources.get(resourceIndex).getName();
|
return "resource name: " + resources.get(resourceIndex).getName();
|
||||||
} else {
|
} else {
|
||||||
return String.format("resource[%s]", resourceIndex);
|
return String.format("%s[%s]", resourcesType, resourceIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -838,11 +919,12 @@ public abstract class DataSourceContractTest {
|
|||||||
private @MonotonicNonNull Uri resolvedUri;
|
private @MonotonicNonNull Uri resolvedUri;
|
||||||
private Map<String, List<String>> responseHeaders;
|
private Map<String, List<String>> responseHeaders;
|
||||||
private Set<String> unexpectedResponseHeaderKeys;
|
private Set<String> unexpectedResponseHeaderKeys;
|
||||||
private byte @MonotonicNonNull [] expectedBytes;
|
private byte[] expectedBytes;
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
responseHeaders = ImmutableMap.of();
|
responseHeaders = ImmutableMap.of();
|
||||||
unexpectedResponseHeaderKeys = ImmutableSet.of();
|
unexpectedResponseHeaderKeys = ImmutableSet.of();
|
||||||
|
expectedBytes = Util.EMPTY_BYTE_ARRAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -909,15 +991,10 @@ public abstract class DataSourceContractTest {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Sets the expected contents of this resource. Defaults to an empty byte array. */
|
||||||
* Sets the expected contents of this resource.
|
|
||||||
*
|
|
||||||
* <p>Must be at least 5 bytes.
|
|
||||||
*/
|
|
||||||
@CanIgnoreReturnValue
|
@CanIgnoreReturnValue
|
||||||
public Builder setExpectedBytes(byte[] expectedBytes) {
|
public Builder setExpectedBytes(byte[] expectedBytes) {
|
||||||
checkArgument(expectedBytes.length >= 5);
|
this.expectedBytes = checkNotNull(expectedBytes);
|
||||||
this.expectedBytes = expectedBytes;
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -928,7 +1005,7 @@ public abstract class DataSourceContractTest {
|
|||||||
resolvedUri != null ? resolvedUri : uri,
|
resolvedUri != null ? resolvedUri : uri,
|
||||||
ImmutableMap.copyOf(responseHeaders),
|
ImmutableMap.copyOf(responseHeaders),
|
||||||
ImmutableSet.copyOf(unexpectedResponseHeaderKeys),
|
ImmutableSet.copyOf(unexpectedResponseHeaderKeys),
|
||||||
checkNotNull(expectedBytes));
|
expectedBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,11 @@ import static androidx.media3.test.utils.WebServerDispatcher.getRequestPath;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
import androidx.media3.datasource.HttpDataSource;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableListMultimap;
|
import com.google.common.collect.ImmutableListMultimap;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.google.common.net.HttpHeaders;
|
import com.google.common.net.HttpHeaders;
|
||||||
@ -119,6 +121,28 @@ public class HttpDataSourceTestEnv extends ExternalResource {
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ImmutableList<DataSourceContractTest.TestResource> getNotFoundResources() {
|
||||||
|
return ImmutableList.of(
|
||||||
|
new DataSourceContractTest.TestResource.Builder()
|
||||||
|
.setName("404")
|
||||||
|
.setUri(Uri.parse(originServer.url("/not/a/real/path").toString()))
|
||||||
|
.setResponseHeaders(
|
||||||
|
ImmutableMap.of(
|
||||||
|
HttpHeaders.CONTENT_LENGTH,
|
||||||
|
ImmutableList.of(String.valueOf(WebServerDispatcher.NOT_FOUND_BODY.length()))))
|
||||||
|
.setExpectedBytes(Util.getUtf8Bytes(WebServerDispatcher.NOT_FOUND_BODY))
|
||||||
|
.build(),
|
||||||
|
new DataSourceContractTest.TestResource.Builder()
|
||||||
|
.setName("no-connection")
|
||||||
|
.setUri(Uri.parse("http://not-a-real-server.test/path"))
|
||||||
|
.setUnexpectedResponseHeaderKeys(ImmutableSet.of(HttpHeaders.CONTENT_LENGTH))
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #getNotFoundResources()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public String getNonexistentUrl() {
|
public String getNonexistentUrl() {
|
||||||
return originServer.url("/not/a/real/path").toString();
|
return originServer.url("/not/a/real/path").toString();
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public class WebServerDispatcher extends Dispatcher {
|
public class WebServerDispatcher extends Dispatcher {
|
||||||
|
|
||||||
|
/** The body associated with a response for an unrecognized path. */
|
||||||
|
public static final String NOT_FOUND_BODY = "Resource not found!";
|
||||||
|
|
||||||
/** A resource served by {@link WebServerDispatcher}. */
|
/** A resource served by {@link WebServerDispatcher}. */
|
||||||
public static class Resource {
|
public static class Resource {
|
||||||
|
|
||||||
@ -294,7 +297,7 @@ public class WebServerDispatcher extends Dispatcher {
|
|||||||
String requestPath = getRequestPath(request);
|
String requestPath = getRequestPath(request);
|
||||||
MockResponse response = new MockResponse();
|
MockResponse response = new MockResponse();
|
||||||
if (!resourcesByPath.containsKey(requestPath)) {
|
if (!resourcesByPath.containsKey(requestPath)) {
|
||||||
return response.setResponseCode(404);
|
return response.setBody(NOT_FOUND_BODY).setResponseCode(404);
|
||||||
}
|
}
|
||||||
Resource resource = checkNotNull(resourcesByPath.get(requestPath));
|
Resource resource = checkNotNull(resourcesByPath.get(requestPath));
|
||||||
for (Map.Entry<String, String> extraHeader : resource.getExtraResponseHeaders().entries()) {
|
for (Map.Entry<String, String> extraHeader : resource.getExtraResponseHeaders().entries()) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user