diff options
Diffstat (limited to 'tests/unit/src/com/android/tv/data/ProgramDataManagerTest.java')
-rw-r--r-- | tests/unit/src/com/android/tv/data/ProgramDataManagerTest.java | 542 |
1 files changed, 0 insertions, 542 deletions
diff --git a/tests/unit/src/com/android/tv/data/ProgramDataManagerTest.java b/tests/unit/src/com/android/tv/data/ProgramDataManagerTest.java deleted file mode 100644 index 5457051f..00000000 --- a/tests/unit/src/com/android/tv/data/ProgramDataManagerTest.java +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (C) 2015 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.android.tv.data; - -import static android.support.test.InstrumentationRegistry.getTargetContext; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.database.ContentObserver; -import android.database.Cursor; -import android.media.tv.TvContract; -import android.net.Uri; -import android.os.HandlerThread; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.test.mock.MockContentProvider; -import android.test.mock.MockContentResolver; -import android.test.mock.MockCursor; -import android.util.Log; -import android.util.SparseArray; - -import com.android.tv.testing.Constants; -import com.android.tv.testing.FakeClock; -import com.android.tv.testing.ProgramInfo; -import com.android.tv.util.Utils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Test for {@link com.android.tv.data.ProgramDataManager} - */ -@SmallTest -public class ProgramDataManagerTest { - private static final boolean DEBUG = false; - private static final String TAG = "ProgramDataManagerTest"; - - // Wait time for expected success. - private static final long WAIT_TIME_OUT_MS = 1000L; - // Wait time for expected failure. - private static final long FAILURE_TIME_OUT_MS = 300L; - - // TODO: Use TvContract constants, once they become public. - private static final String PARAM_CHANNEL = "channel"; - private static final String PARAM_START_TIME = "start_time"; - private static final String PARAM_END_TIME = "end_time"; - - private ProgramDataManager mProgramDataManager; - private FakeClock mClock; - private HandlerThread mHandlerThread; - private TestProgramDataManagerListener mListener; - private FakeContentResolver mContentResolver; - private FakeContentProvider mContentProvider; - - @Before - public void setUp() { - mClock = FakeClock.createWithCurrentTime(); - mListener = new TestProgramDataManagerListener(); - mContentProvider = new FakeContentProvider(getTargetContext()); - mContentResolver = new FakeContentResolver(); - mContentResolver.addProvider(TvContract.AUTHORITY, mContentProvider); - mHandlerThread = new HandlerThread(TAG); - mHandlerThread.start(); - mProgramDataManager = new ProgramDataManager( - mContentResolver, mClock, mHandlerThread.getLooper()); - mProgramDataManager.setPrefetchEnabled(true); - mProgramDataManager.addListener(mListener); - } - - @After - public void tearDown() { - mHandlerThread.quitSafely(); - mProgramDataManager.stop(); - } - - private void startAndWaitForComplete() throws InterruptedException { - mProgramDataManager.start(); - assertTrue(mListener.programUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); - } - - /** - * Test for {@link ProgramInfo#getIndex} and {@link ProgramInfo#getStartTimeMs}. - */ - @Test - public void testProgramUtils() { - ProgramInfo stub = ProgramInfo.create(); - for (long channelId = 1; channelId < Constants.UNIT_TEST_CHANNEL_COUNT; channelId++) { - int index = stub.getIndex(mClock.currentTimeMillis(), channelId); - long startTimeMs = stub.getStartTimeMs(index, channelId); - ProgramInfo programAt = stub.build(InstrumentationRegistry.getContext(), index); - assertTrue(startTimeMs <= mClock.currentTimeMillis()); - assertTrue(mClock.currentTimeMillis() < startTimeMs + programAt.durationMs); - } - } - - /** - * Test for following methods. - * - * <p> - * {@link ProgramDataManager#getCurrentProgram(long)}, - * {@link ProgramDataManager#getPrograms(long, long)}, - * {@link ProgramDataManager#setPrefetchTimeRange(long)}. - * </p> - */ - @Test - public void testGetPrograms() throws InterruptedException { - // Initial setup to test {@link ProgramDataManager#setPrefetchTimeRange(long)}. - long preventSnapDelayMs = ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS * 2; - long prefetchTimeRangeStartMs = System.currentTimeMillis() + preventSnapDelayMs; - mClock.setCurrentTimeMillis(prefetchTimeRangeStartMs + preventSnapDelayMs); - mProgramDataManager.setPrefetchTimeRange(prefetchTimeRangeStartMs); - - startAndWaitForComplete(); - - for (long channelId = 1; channelId <= Constants.UNIT_TEST_CHANNEL_COUNT; channelId++) { - Program currentProgram = mProgramDataManager.getCurrentProgram(channelId); - // Test {@link ProgramDataManager#getCurrentProgram(long)}. - assertTrue(currentProgram.getStartTimeUtcMillis() <= mClock.currentTimeMillis() - && mClock.currentTimeMillis() <= currentProgram.getEndTimeUtcMillis()); - - // Test {@link ProgramDataManager#getPrograms(long)}. - // Case #1: Normal case - List<Program> programs = - mProgramDataManager.getPrograms(channelId, mClock.currentTimeMillis()); - ProgramInfo stub = ProgramInfo.create(); - int index = stub.getIndex(mClock.currentTimeMillis(), channelId); - for (Program program : programs) { - ProgramInfo programInfoAt = stub.build(InstrumentationRegistry.getContext(), index); - long startTimeMs = stub.getStartTimeMs(index, channelId); - assertProgramEquals(startTimeMs, programInfoAt, program); - index++; - } - // Case #2: Corner cases where there's a program that starts at the start of the range. - long startTimeMs = programs.get(0).getStartTimeUtcMillis(); - programs = mProgramDataManager.getPrograms(channelId, startTimeMs); - assertEquals(startTimeMs, programs.get(0).getStartTimeUtcMillis()); - - // Test {@link ProgramDataManager#setPrefetchTimeRange(long)}. - programs = mProgramDataManager.getPrograms(channelId, - prefetchTimeRangeStartMs - TimeUnit.HOURS.toMillis(1)); - for (Program program : programs) { - assertTrue(program.getEndTimeUtcMillis() >= prefetchTimeRangeStartMs); - } - } - } - - /** - * Test for following methods. - * - * <p> - * {@link ProgramDataManager#addOnCurrentProgramUpdatedListener}, - * {@link ProgramDataManager#removeOnCurrentProgramUpdatedListener}. - * </p> - */ - @Test - public void testCurrentProgramListener() throws InterruptedException { - final long testChannelId = 1; - ProgramInfo stub = ProgramInfo.create(); - int index = stub.getIndex(mClock.currentTimeMillis(), testChannelId); - // Set current time to few seconds before the current program ends, - // so we can see if callback is called as expected. - long nextProgramStartTimeMs = stub.getStartTimeMs(index + 1, testChannelId); - ProgramInfo nextProgramInfo = stub.build(InstrumentationRegistry.getContext(), index + 1); - mClock.setCurrentTimeMillis(nextProgramStartTimeMs - (WAIT_TIME_OUT_MS / 2)); - - startAndWaitForComplete(); - // Note that changing current time doesn't affect the current program - // because current program is updated after waiting for the program's duration. - // See {@link ProgramDataManager#updateCurrentProgram}. - mClock.setCurrentTimeMillis(mClock.currentTimeMillis() + WAIT_TIME_OUT_MS); - TestProgramDataManagerOnCurrentProgramUpdatedListener listener = - new TestProgramDataManagerOnCurrentProgramUpdatedListener(); - mProgramDataManager.addOnCurrentProgramUpdatedListener(testChannelId, listener); - assertTrue( - listener.currentProgramUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); - assertEquals(testChannelId, listener.updatedChannelId); - Program currentProgram = mProgramDataManager.getCurrentProgram(testChannelId); - assertProgramEquals(nextProgramStartTimeMs, nextProgramInfo, currentProgram); - assertEquals(listener.updatedProgram, currentProgram); - } - - /** - * Test if program data is refreshed after the program insertion. - */ - @Test - public void testContentProviderUpdate() throws InterruptedException { - final long testChannelId = 1; - startAndWaitForComplete(); - // Force program data manager to update program data whenever it's changes. - mProgramDataManager.setProgramPrefetchUpdateWait(0); - mListener.reset(); - List<Program> programList = - mProgramDataManager.getPrograms(testChannelId, mClock.currentTimeMillis()); - assertNotNull(programList); - long lastProgramEndTime = programList.get(programList.size() - 1).getEndTimeUtcMillis(); - // Make change in content provider - mContentProvider.simulateAppend(testChannelId); - assertTrue(mListener.programUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); - programList = mProgramDataManager.getPrograms(testChannelId, mClock.currentTimeMillis()); - assertTrue( - lastProgramEndTime < programList.get(programList.size() - 1).getEndTimeUtcMillis()); - } - - /** - * Test for {@link ProgramDataManager#setPauseProgramUpdate(boolean)}. - */ - @Test - public void testSetPauseProgramUpdate() throws InterruptedException { - final long testChannelId = 1; - startAndWaitForComplete(); - // Force program data manager to update program data whenever it's changes. - mProgramDataManager.setProgramPrefetchUpdateWait(0); - mListener.reset(); - mProgramDataManager.setPauseProgramUpdate(true); - mContentProvider.simulateAppend(testChannelId); - assertFalse(mListener.programUpdatedLatch.await(FAILURE_TIME_OUT_MS, - TimeUnit.MILLISECONDS)); - } - - public static void assertProgramEquals(long expectedStartTime, ProgramInfo expectedInfo, - Program actualProgram) { - assertEquals("title", expectedInfo.title, actualProgram.getTitle()); - assertEquals("episode", expectedInfo.episode, actualProgram.getEpisodeTitle()); - assertEquals("description", expectedInfo.description, actualProgram.getDescription()); - assertEquals("startTime", expectedStartTime, actualProgram.getStartTimeUtcMillis()); - assertEquals("endTime", expectedStartTime + expectedInfo.durationMs, - actualProgram.getEndTimeUtcMillis()); - } - - private class FakeContentResolver extends MockContentResolver { - @Override - public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { - super.notifyChange(uri, observer, syncToNetwork); - if (DEBUG) { - Log.d(TAG, "onChanged(uri=" + uri + ")"); - } - if (observer != null) { - observer.dispatchChange(false, uri); - } else { - mProgramDataManager.getContentObserver().dispatchChange(false, uri); - } - } - } - - private static class ProgramInfoWrapper { - private final int index; - private final long startTimeMs; - private final ProgramInfo programInfo; - - public ProgramInfoWrapper(int index, long startTimeMs, ProgramInfo programInfo) { - this.index = index; - this.startTimeMs = startTimeMs; - this.programInfo = programInfo; - } - } - - // This implements the minimal methods in content resolver - // and detailed assumptions are written in each method. - private class FakeContentProvider extends MockContentProvider { - private final SparseArray<List<ProgramInfoWrapper>> mProgramInfoList = new SparseArray<>(); - - /** - * Constructor for FakeContentProvider - * <p> - * This initializes program info assuming that - * channel IDs are 1, 2, 3, ... {@link Constants#UNIT_TEST_CHANNEL_COUNT}. - * </p> - */ - public FakeContentProvider(Context context) { - super(context); - long startTimeMs = Utils.floorTime( - mClock.currentTimeMillis() - ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS, - ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS); - long endTimeMs = startTimeMs + (ProgramDataManager.PROGRAM_GUIDE_MAX_TIME_RANGE / 2); - for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) { - List<ProgramInfoWrapper> programInfoList = new ArrayList<>(); - ProgramInfo stub = ProgramInfo.create(); - int index = stub.getIndex(startTimeMs, i); - long programStartTimeMs = stub.getStartTimeMs(index, i); - while (programStartTimeMs < endTimeMs) { - ProgramInfo programAt = stub.build(InstrumentationRegistry.getContext(), index); - programInfoList.add( - new ProgramInfoWrapper(index, programStartTimeMs, programAt)); - index++; - programStartTimeMs += programAt.durationMs; - } - mProgramInfoList.put(i, programInfoList); - } - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder) { - if (DEBUG) { - Log.d(TAG, "dump query"); - Log.d(TAG, " uri=" + uri); - Log.d(TAG, " projection=" + Arrays.toString(projection)); - Log.d(TAG, " selection=" + selection); - } - long startTimeMs = Long.parseLong(uri.getQueryParameter(PARAM_START_TIME)); - long endTimeMs = Long.parseLong(uri.getQueryParameter(PARAM_END_TIME)); - if (startTimeMs == 0 || endTimeMs == 0) { - throw new UnsupportedOperationException(); - } - assertProgramUri(uri); - long channelId; - try { - channelId = Long.parseLong(uri.getQueryParameter(PARAM_CHANNEL)); - } catch (NumberFormatException e) { - channelId = -1; - } - return new FakeCursor(projection, channelId, startTimeMs, endTimeMs); - } - - /** - * Simulate program data appends at the end of the existing programs. - * This appends programs until the maximum program query range - * ({@link ProgramDataManager#PROGRAM_GUIDE_MAX_TIME_RANGE}) - * where we started with the inserting half of it. - */ - public void simulateAppend(long channelId) { - long endTimeMs = - mClock.currentTimeMillis() + ProgramDataManager.PROGRAM_GUIDE_MAX_TIME_RANGE; - List<ProgramInfoWrapper> programList = mProgramInfoList.get((int) channelId); - if (mProgramInfoList == null) { - return; - } - ProgramInfo stub = ProgramInfo.create(); - ProgramInfoWrapper last = programList.get(programList.size() - 1); - while (last.startTimeMs < endTimeMs) { - ProgramInfo nextProgramInfo = stub.build(InstrumentationRegistry.getContext(), - last.index + 1); - ProgramInfoWrapper next = new ProgramInfoWrapper(last.index + 1, - last.startTimeMs + last.programInfo.durationMs, nextProgramInfo); - programList.add(next); - last = next; - } - mContentResolver.notifyChange(TvContract.Programs.CONTENT_URI, null); - } - - private void assertProgramUri(Uri uri) { - assertTrue("Uri(" + uri + ") isn't channel uri", - uri.toString().startsWith(TvContract.Programs.CONTENT_URI.toString())); - } - - public ProgramInfoWrapper get(long channelId, int position) { - List<ProgramInfoWrapper> programList = mProgramInfoList.get((int) channelId); - if (programList == null || position >= programList.size()) { - return null; - } - return programList.get(position); - } - } - - private class FakeCursor extends MockCursor { - private final String[] ALL_COLUMNS = { - TvContract.Programs.COLUMN_CHANNEL_ID, - TvContract.Programs.COLUMN_TITLE, - TvContract.Programs.COLUMN_SHORT_DESCRIPTION, - TvContract.Programs.COLUMN_EPISODE_TITLE, - TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, - TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS}; - private final String[] mColumns; - private final boolean mIsQueryForSingleChannel; - private final long mStartTimeMs; - private final long mEndTimeMs; - private final int mCount; - private long mChannelId; - private int mProgramPosition; - private ProgramInfoWrapper mCurrentProgram; - - /** - * Constructor - * @param columns the same as projection passed from {@link FakeContentProvider#query}. - * Can be null for query all. - * @param channelId channel ID to query programs belongs to the specified channel. - * Can be negative to indicate all channels. - * @param startTimeMs start of the time range to query programs. - * @param endTimeMs end of the time range to query programs. - */ - public FakeCursor(String[] columns, long channelId, long startTimeMs, long endTimeMs) { - mColumns = (columns == null) ? ALL_COLUMNS : columns; - mIsQueryForSingleChannel = (channelId > 0); - mChannelId = channelId; - mProgramPosition = -1; - mStartTimeMs = startTimeMs; - mEndTimeMs = endTimeMs; - int count = 0; - while (moveToNext()) { - count++; - } - mCount = count; - // Rewind channel Id and program index. - mChannelId = channelId; - mProgramPosition = -1; - if (DEBUG) { - Log.d(TAG, "FakeCursor(columns=" + Arrays.toString(columns) - + ", channelId=" + channelId + ", startTimeMs=" + startTimeMs - + ", endTimeMs=" + endTimeMs + ") has mCount=" + mCount); - } - } - - @Override - public String getColumnName(int columnIndex) { - return mColumns[columnIndex]; - } - - @Override - public int getColumnIndex(String columnName) { - for (int i = 0; i < mColumns.length; i++) { - if (mColumns[i].equalsIgnoreCase(columnName)) { - return i; - } - } - return -1; - } - - @Override - public int getInt(int columnIndex) { - if (DEBUG) { - Log.d(TAG, "Column (" + getColumnName(columnIndex) + ") is ignored in getInt()"); - } - return 0; - } - - @Override - public long getLong(int columnIndex) { - String columnName = getColumnName(columnIndex); - switch (columnName) { - case TvContract.Programs.COLUMN_CHANNEL_ID: - return mChannelId; - case TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS: - return mCurrentProgram.startTimeMs; - case TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS: - return mCurrentProgram.startTimeMs + mCurrentProgram.programInfo.durationMs; - } - if (DEBUG) { - Log.d(TAG, "Column (" + columnName + ") is ignored in getLong()"); - } - return 0; - } - - @Override - public String getString(int columnIndex) { - String columnName = getColumnName(columnIndex); - switch (columnName) { - case TvContract.Programs.COLUMN_TITLE: - return mCurrentProgram.programInfo.title; - case TvContract.Programs.COLUMN_SHORT_DESCRIPTION: - return mCurrentProgram.programInfo.description; - case TvContract.Programs.COLUMN_EPISODE_TITLE: - return mCurrentProgram.programInfo.episode; - } - if (DEBUG) { - Log.d(TAG, "Column (" + columnName + ") is ignored in getString()"); - } - return null; - } - - @Override - public int getCount() { - return mCount; - } - - @Override - public boolean moveToNext() { - while (true) { - ProgramInfoWrapper program = mContentProvider.get(mChannelId, ++mProgramPosition); - if (program == null || program.startTimeMs >= mEndTimeMs) { - if (mIsQueryForSingleChannel) { - return false; - } else { - if (++mChannelId > Constants.UNIT_TEST_CHANNEL_COUNT) { - return false; - } - mProgramPosition = -1; - } - } else if (program.startTimeMs + program.programInfo.durationMs >= mStartTimeMs) { - mCurrentProgram = program; - break; - } - } - return true; - } - - @Override - public void close() { - // No-op. - } - } - - private class TestProgramDataManagerListener implements ProgramDataManager.Listener { - public CountDownLatch programUpdatedLatch = new CountDownLatch(1); - - @Override - public void onProgramUpdated() { - programUpdatedLatch.countDown(); - } - - public void reset() { - programUpdatedLatch = new CountDownLatch(1); - } - } - - private class TestProgramDataManagerOnCurrentProgramUpdatedListener implements - OnCurrentProgramUpdatedListener { - public final CountDownLatch currentProgramUpdatedLatch = new CountDownLatch(1); - public long updatedChannelId = -1; - public Program updatedProgram = null; - - @Override - public void onCurrentProgramUpdated(long channelId, Program program) { - updatedChannelId = channelId; - updatedProgram = program; - currentProgramUpdatedLatch.countDown(); - } - } -} |