Fix ContentDataSource and enhance tests to validate read data

This commit is contained in:
Oliver Woodman 2017-06-23 17:20:51 +01:00
parent 2da22e9e0f
commit 8bb643976f
8 changed files with 185 additions and 123 deletions

View File

@ -25,8 +25,8 @@
tools:ignore="MissingApplicationIcon,HardcodedDebugMode"> tools:ignore="MissingApplicationIcon,HardcodedDebugMode">
<uses-library android:name="android.test.runner"/> <uses-library android:name="android.test.runner"/>
<provider <provider
android:authorities="exoplayer" android:authorities="com.google.android.exoplayer2.core.test"
android:name="com.google.android.exoplayer2.upstream.TestDataProvider"/> android:name="com.google.android.exoplayer2.upstream.ContentDataSourceTest$TestContentProvider"/>
</application> </application>
<instrumentation <instrumentation

View File

@ -1,16 +0,0 @@
package com.google.android.exoplayer2.upstream;
import android.content.ContentResolver;
import android.net.Uri;
final class AndroidDataSourceConstants {
static final long SAMPLE_MP4_BYTES = 101597;
static final String SAMPLE_MP4_PATH = "/mp4/sample.mp4";
static final String TEST_DATA_PROVIDER_AUTHORITY = "exoplayer";
static final Uri NULL_DESCRIPTOR_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(TEST_DATA_PROVIDER_AUTHORITY)
.build();
private AndroidDataSourceConstants() {}
}

View File

@ -1,21 +1,62 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.upstream; package com.google.android.exoplayer2.upstream;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import com.google.android.exoplayer2.testutil.TestUtil;
import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_BYTES; /**
import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_PATH; * Unit tests for {@link AssetDataSource}.
*/
public final class AssetDataSourceTest extends InstrumentationTestCase {
public class AssetDataSourceTest extends InstrumentationTestCase { private static final String DATA_PATH = "binary/1024_incrementing_bytes.mp3";
private static final long DATA_LENGTH = 1024;
public void testAssetDataSource() throws Exception { public void testReadFileUri() throws Exception {
final Context context = getInstrumentation().getContext(); Context context = getInstrumentation().getContext();
AssetDataSource dataSource = new AssetDataSource(context); AssetDataSource dataSource = new AssetDataSource(context);
Uri assetUri = Uri.parse("file:///android_asset" + SAMPLE_MP4_PATH); Uri assetUri = Uri.parse("file:///android_asset/" + DATA_PATH);
DataSpec dataSpec = new DataSpec(assetUri); DataSpec dataSpec = new DataSpec(assetUri);
long sourceLengthBytes = dataSource.open(dataSpec); try {
long length = dataSource.open(dataSpec);
assertEquals(SAMPLE_MP4_BYTES, sourceLengthBytes); assertEquals(DATA_LENGTH, length);
byte[] readData = TestUtil.readToEnd(dataSource);
MoreAsserts.assertEquals(TestUtil.getByteArray(getInstrumentation(), DATA_PATH), readData);
} finally {
dataSource.close();
} }
}
public void testReadAssetUri() throws Exception {
Context context = getInstrumentation().getContext();
AssetDataSource dataSource = new AssetDataSource(context);
Uri assetUri = Uri.parse("asset:///" + DATA_PATH);
DataSpec dataSpec = new DataSpec(assetUri);
try {
long length = dataSource.open(dataSpec);
assertEquals(DATA_LENGTH, length);
byte[] readData = TestUtil.readToEnd(dataSource);
MoreAsserts.assertEquals(TestUtil.getByteArray(getInstrumentation(), DATA_PATH), readData);
} finally {
dataSource.close();
}
}
} }

View File

@ -1,40 +1,127 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.android.exoplayer2.upstream; package com.google.android.exoplayer2.upstream;
import android.content.ContentProvider;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.ContentValues;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.NonNull;
import android.test.InstrumentationTestCase; import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import com.google.android.exoplayer2.testutil.TestUtil;
import java.io.FileNotFoundException;
import java.io.IOException;
import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.NULL_DESCRIPTOR_URI; /**
import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_BYTES; * Unit tests for {@link ContentDataSource}.
import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.SAMPLE_MP4_PATH; */
import static com.google.android.exoplayer2.upstream.AndroidDataSourceConstants.TEST_DATA_PROVIDER_AUTHORITY; public final class ContentDataSourceTest extends InstrumentationTestCase {
public class ContentDataSourceTest extends InstrumentationTestCase { private static final String DATA_PATH = "binary/1024_incrementing_bytes.mp3";
private static final long DATA_LENGTH = 1024;
public void testValidContentDataSource() throws Exception { public void testReadValidUri() throws Exception {
Context context = getInstrumentation().getContext(); ContentDataSource dataSource = new ContentDataSource(getInstrumentation().getContext());
ContentDataSource dataSource = new ContentDataSource(context);
Uri contentUri = new Uri.Builder() Uri contentUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT) .scheme(ContentResolver.SCHEME_CONTENT)
.authority(TEST_DATA_PROVIDER_AUTHORITY) .authority(TestContentProvider.AUTHORITY)
.path(SAMPLE_MP4_PATH).build(); .path(DATA_PATH).build();
DataSpec dataSpec = new DataSpec(contentUri); DataSpec dataSpec = new DataSpec(contentUri);
long sourceLengthBytes = dataSource.open(dataSpec); try {
long length = dataSource.open(dataSpec);
assertEquals(SAMPLE_MP4_BYTES, sourceLengthBytes); assertEquals(DATA_LENGTH, length);
byte[] readData = TestUtil.readToEnd(dataSource);
MoreAsserts.assertEquals(TestUtil.getByteArray(getInstrumentation(), DATA_PATH), readData);
} finally {
dataSource.close();
}
} }
public void testNullContentDataSource() throws Exception { public void testReadInvalidUri() throws Exception {
Context context = getInstrumentation().getContext(); ContentDataSource dataSource = new ContentDataSource(getInstrumentation().getContext());
ContentDataSource dataSource = new ContentDataSource(context); Uri contentUri = new Uri.Builder()
DataSpec dataSpec = new DataSpec(NULL_DESCRIPTOR_URI); .scheme(ContentResolver.SCHEME_CONTENT)
.authority(TestContentProvider.AUTHORITY)
.build();
DataSpec dataSpec = new DataSpec(contentUri);
try { try {
dataSource.open(dataSpec); dataSource.open(dataSpec);
fail("Expected exception not thrown."); fail();
} catch (ContentDataSource.ContentDataSourceException e) { } catch (ContentDataSource.ContentDataSourceException e) {
// Expected. // Expected.
} finally {
dataSource.close();
} }
} }
public static final class TestContentProvider extends ContentProvider {
private static final String AUTHORITY = "com.google.android.exoplayer2.core.test";
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
throw new UnsupportedOperationException();
}
@Override
public AssetFileDescriptor openAssetFile(@NonNull Uri uri, @NonNull String mode)
throws FileNotFoundException {
if (uri.getPath() == null) {
return null;
}
try {
return getContext().getAssets().openFd(uri.getPath().replaceFirst("/", ""));
} catch (IOException e) {
FileNotFoundException exception = new FileNotFoundException(e.getMessage());
exception.initCause(e);
throw exception;
}
}
@Override
public String getType(@NonNull Uri uri) {
throw new UnsupportedOperationException();
}
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
}
@Override
public int delete(@NonNull Uri uri, String selection,
String[] selectionArgs) {
throw new UnsupportedOperationException();
}
@Override
public int update(@NonNull Uri uri, ContentValues values,
String selection, String[] selectionArgs) {
throw new UnsupportedOperationException();
}
}
} }

View File

@ -1,68 +0,0 @@
package com.google.android.exoplayer2.upstream;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import java.io.FileNotFoundException;
import java.io.IOException;
import static junit.framework.Assert.assertNotNull;
public class TestDataProvider extends ContentProvider {
@Override
public boolean onCreate() {
return true;
}
@Nullable
@Override
public Cursor query(@NonNull final Uri uri, @Nullable final String[] projection, @Nullable final String selection, @Nullable final String[] selectionArgs, @Nullable final String sortOrder) {
throw new UnsupportedOperationException("Not implemented");
}
@Nullable
@Override
public AssetFileDescriptor openAssetFile(@NonNull final Uri uri, @NonNull final String mode) throws FileNotFoundException {
if (uri.equals(AndroidDataSourceConstants.NULL_DESCRIPTOR_URI)) {
return null;
}
try {
Context context = getContext();
assertNotNull(context);
return context.getAssets().openFd(uri.getPath().replaceFirst("/", ""));
} catch (IOException e) {
FileNotFoundException exception = new FileNotFoundException(e.getMessage());
exception.initCause(e);
throw exception;
}
}
@Nullable
@Override
public String getType(@NonNull final Uri uri) {
throw new UnsupportedOperationException("Not implemented");
}
@Nullable
@Override
public Uri insert(@NonNull final Uri uri, @Nullable final ContentValues values) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public int delete(@NonNull final Uri uri, @Nullable final String selection, @Nullable final String[] selectionArgs) {
throw new UnsupportedOperationException("Not implemented");
}
@Override
public int update(@NonNull final Uri uri, @Nullable final ContentValues values, @Nullable final String selection, @Nullable final String[] selectionArgs) {
throw new UnsupportedOperationException("Not implemented");
}
}

View File

@ -76,8 +76,9 @@ public final class ContentDataSource implements DataSource {
throw new FileNotFoundException("Could not open file descriptor for: " + uri); throw new FileNotFoundException("Could not open file descriptor for: " + uri);
} }
inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor()); inputStream = new FileInputStream(assetFileDescriptor.getFileDescriptor());
long skipped = inputStream.skip(dataSpec.position); long assertStartOffset = assetFileDescriptor.getStartOffset();
if (skipped < dataSpec.position) { long skipped = inputStream.skip(assertStartOffset + dataSpec.position) - assertStartOffset;
if (skipped != dataSpec.position) {
// We expect the skip to be satisfied in full. If it isn't then we're probably trying to // We expect the skip to be satisfied in full. If it isn't then we're probably trying to
// skip beyond the end of the data. // skip beyond the end of the data.
throw new EOFException(); throw new EOFException();

View File

@ -22,6 +22,7 @@ import com.google.android.exoplayer2.extractor.Extractor;
import com.google.android.exoplayer2.extractor.PositionHolder; import com.google.android.exoplayer2.extractor.PositionHolder;
import com.google.android.exoplayer2.extractor.SeekMap; import com.google.android.exoplayer2.extractor.SeekMap;
import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException; import com.google.android.exoplayer2.testutil.FakeExtractorInput.SimulatedIOException;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
@ -64,6 +65,22 @@ public class TestUtil {
} }
} }
public static byte[] readToEnd(DataSource dataSource) throws IOException {
byte[] data = new byte[1024];
int position = 0;
int bytesRead = 0;
while (bytesRead != C.RESULT_END_OF_INPUT) {
if (position == data.length) {
data = Arrays.copyOf(data, data.length * 2);
}
bytesRead = dataSource.read(data, position, data.length - position);
if (bytesRead != C.RESULT_END_OF_INPUT) {
position += bytesRead;
}
}
return Arrays.copyOf(data, position);
}
public static FakeExtractorOutput consumeTestData(Extractor extractor, FakeExtractorInput input, public static FakeExtractorOutput consumeTestData(Extractor extractor, FakeExtractorInput input,
long timeUs) throws IOException, InterruptedException { long timeUs) throws IOException, InterruptedException {
return consumeTestData(extractor, input, timeUs, false); return consumeTestData(extractor, input, timeUs, false);