1816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/* 2816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Copyright (C) 2015 The Android Open Source Project 3816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 4816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Licensed under the Apache License, Version 2.0 (the "License"); 5816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * you may not use this file except in compliance with the License. 6816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * You may obtain a copy of the License at 7816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 8816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * http://www.apache.org/licenses/LICENSE-2.0 9816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 10816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Unless required by applicable law or agreed to in writing, software 11816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * distributed under the License is distributed on an "AS IS" BASIS, 12816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * See the License for the specific language governing permissions and 14816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * limitations under the License. 15816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 16816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 17816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopackage com.android.tv.data; 18816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 19816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.content.Context; 20816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.database.ContentObserver; 21816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.database.Cursor; 22816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.media.tv.TvContract; 23816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.net.Uri; 24816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.os.HandlerThread; 25816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.test.AndroidTestCase; 26816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.test.mock.MockContentProvider; 27816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.test.mock.MockContentResolver; 28816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.test.mock.MockCursor; 29816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.test.suitebuilder.annotation.SmallTest; 30816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.Log; 31816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport android.util.SparseArray; 32816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 33816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.testing.Constants; 34816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.testing.ProgramInfo; 351abddd9f6225298066094e20a6c29061b6af4590Nick Chalkoimport com.android.tv.testing.FakeClock; 36816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport com.android.tv.util.Utils; 37816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 38816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.ArrayList; 3907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalkoimport java.util.Arrays; 40816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.List; 41816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.concurrent.CountDownLatch; 42816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkoimport java.util.concurrent.TimeUnit; 43816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 44816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko/** 45816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Test for {@link com.android.tv.data.ProgramDataManager} 46816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 47816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko@SmallTest 48816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalkopublic class ProgramDataManagerTest extends AndroidTestCase { 49816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final boolean DEBUG = false; 50816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final String TAG = "ProgramDataManagerTest"; 51816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 52816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Wait time for expected success. 53816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long WAIT_TIME_OUT_MS = 1000L; 54816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Wait time for expected failure. 55816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final long FAILURE_TIME_OUT_MS = 300L; 56816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 57816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // TODO: Use TvContract constants, once they become public. 58816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final String PARAM_CHANNEL = "channel"; 59816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final String PARAM_START_TIME = "start_time"; 60816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static final String PARAM_END_TIME = "end_time"; 61816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 62816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private ProgramDataManager mProgramDataManager; 63816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private FakeClock mClock; 64816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private HandlerThread mHandlerThread; 65816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private TestProgramDataManagerListener mListener; 66816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private FakeContentResolver mContentResolver; 67816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private FakeContentProvider mContentProvider; 68816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 69816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 70816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko protected void setUp() throws Exception { 71816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko super.setUp(); 72816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 731abddd9f6225298066094e20a6c29061b6af4590Nick Chalko mClock = FakeClock.createWithCurrentTime(); 74816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener = new TestProgramDataManagerListener(); 75816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentProvider = new FakeContentProvider(getContext()); 76816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentResolver = new FakeContentResolver(); 77816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentResolver.addProvider(TvContract.AUTHORITY, mContentProvider); 78816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandlerThread = new HandlerThread(TAG); 79816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandlerThread.start(); 80816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager = new ProgramDataManager( 81816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentResolver, mClock, mHandlerThread.getLooper()); 827d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko mProgramDataManager.setPrefetchEnabled(true); 83816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.addListener(mListener); 84816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 85816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 86816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 87816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko protected void tearDown() throws Exception { 88816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko super.tearDown(); 89816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mHandlerThread.quitSafely(); 90816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.stop(); 91816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 92816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 93816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void startAndWaitForComplete() throws Exception { 94816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.start(); 95816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertTrue(mListener.programUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 96816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 97816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 98816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 99816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Test for {@link ProgramInfo#getIndex} and {@link ProgramInfo#getStartTimeMs}. 100816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 101816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void testProgramUtils() { 102816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo stub = ProgramInfo.create(); 103816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (long channelId = 1; channelId < Constants.UNIT_TEST_CHANNEL_COUNT; channelId++) { 104816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int index = stub.getIndex(mClock.currentTimeMillis(), channelId); 105816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startTimeMs = stub.getStartTimeMs(index, channelId); 106816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo programAt = stub.build(getContext(), index); 107816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertTrue(startTimeMs <= mClock.currentTimeMillis()); 108816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertTrue(mClock.currentTimeMillis() < startTimeMs + programAt.durationMs); 109816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 110816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 111816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 112816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 113816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Test for following methods. 114816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 115816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * <p> 116816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link ProgramDataManager#getCurrentProgram(long)}, 117816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link ProgramDataManager#getPrograms(long, long)}, 118816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link ProgramDataManager#setPrefetchTimeRange(long)}. 119816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * </p> 120816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 121816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void testGetPrograms() throws Exception { 122816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Initial setup to test {@link ProgramDataManager#setPrefetchTimeRange(long)}. 123816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long preventSnapDelayMs = ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS * 2; 124816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long prefetchTimeRangeStartMs = System.currentTimeMillis() + preventSnapDelayMs; 125816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mClock.setCurrentTimeMillis(prefetchTimeRangeStartMs + preventSnapDelayMs); 126816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.setPrefetchTimeRange(prefetchTimeRangeStartMs); 127816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 128816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startAndWaitForComplete(); 129816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 130816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (long channelId = 1; channelId <= Constants.UNIT_TEST_CHANNEL_COUNT; channelId++) { 131816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Program currentProgram = mProgramDataManager.getCurrentProgram(channelId); 132816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Test {@link ProgramDataManager#getCurrentProgram(long)}. 133816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertTrue(currentProgram.getStartTimeUtcMillis() <= mClock.currentTimeMillis() 134816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko && mClock.currentTimeMillis() <= currentProgram.getEndTimeUtcMillis()); 135816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 136816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Test {@link ProgramDataManager#getPrograms(long)}. 137816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Case #1: Normal case 138816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<Program> programs = 139816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.getPrograms(channelId, mClock.currentTimeMillis()); 140816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo stub = ProgramInfo.create(); 141816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int index = stub.getIndex(mClock.currentTimeMillis(), channelId); 142816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (Program program : programs) { 143816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo programInfoAt = stub.build(getContext(), index); 144816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startTimeMs = stub.getStartTimeMs(index, channelId); 1457d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertProgramEquals(startTimeMs, programInfoAt, program); 146816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko index++; 147816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 148816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Case #2: Corner cases where there's a program that starts at the start of the range. 149816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startTimeMs = programs.get(0).getStartTimeUtcMillis(); 150816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programs = mProgramDataManager.getPrograms(channelId, startTimeMs); 151816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertEquals(startTimeMs, programs.get(0).getStartTimeUtcMillis()); 152816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 153816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Test {@link ProgramDataManager#setPrefetchTimeRange(long)}. 154816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programs = mProgramDataManager.getPrograms(channelId, 155816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko prefetchTimeRangeStartMs - TimeUnit.HOURS.toMillis(1)); 156816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (Program program : programs) { 157816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertTrue(program.getEndTimeUtcMillis() >= prefetchTimeRangeStartMs); 158816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 159816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 160816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 161816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 162816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 163816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Test for following methods. 164816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * 165816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * <p> 166816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link ProgramDataManager#addOnCurrentProgramUpdatedListener}, 167816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * {@link ProgramDataManager#removeOnCurrentProgramUpdatedListener}. 168816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * </p> 169816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 170816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void testCurrentProgramListener() throws Exception { 171816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko final long testChannelId = 1; 172816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo stub = ProgramInfo.create(); 173816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int index = stub.getIndex(mClock.currentTimeMillis(), testChannelId); 174816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Set current time to few seconds before the current program ends, 175816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // so we can see if callback is called as expected. 176816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long nextProgramStartTimeMs = stub.getStartTimeMs(index + 1, testChannelId); 177816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo nextProgramInfo = stub.build(getContext(), index + 1); 178816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mClock.setCurrentTimeMillis(nextProgramStartTimeMs - (WAIT_TIME_OUT_MS / 2)); 179816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 180816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startAndWaitForComplete(); 181816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Note that changing current time doesn't affect the current program 182816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // because current program is updated after waiting for the program's duration. 183816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // See {@link ProgramDataManager#updateCurrentProgram}. 184816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mClock.setCurrentTimeMillis(mClock.currentTimeMillis() + WAIT_TIME_OUT_MS); 185816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TestProgramDataManagerOnCurrentProgramUpdatedListener listener = 186816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko new TestProgramDataManagerOnCurrentProgramUpdatedListener(); 187816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.addOnCurrentProgramUpdatedListener(testChannelId, listener); 1887d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertTrue( 1897d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko listener.currentProgramUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 190816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertEquals(testChannelId, listener.updatedChannelId); 1917d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko Program currentProgram = mProgramDataManager.getCurrentProgram(testChannelId); 1927d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertProgramEquals(nextProgramStartTimeMs, nextProgramInfo, currentProgram); 1937d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertEquals(listener.updatedProgram, currentProgram); 194816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 195816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 196816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 197816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Test if program data is refreshed after the program insertion. 198816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 199816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void testContentProviderUpdate() throws Exception { 200816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko final long testChannelId = 1; 201816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startAndWaitForComplete(); 202816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Force program data manager to update program data whenever it's changes. 203816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.setProgramPrefetchUpdateWait(0); 204816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener.reset(); 205816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<Program> programList = 206816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.getPrograms(testChannelId, mClock.currentTimeMillis()); 207816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertNotNull(programList); 208816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long lastProgramEndTime = programList.get(programList.size() - 1).getEndTimeUtcMillis(); 209816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Make change in content provider 210816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentProvider.simulateAppend(testChannelId); 211816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertTrue(mListener.programUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS)); 212816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programList = mProgramDataManager.getPrograms(testChannelId, mClock.currentTimeMillis()); 2137d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertTrue( 2147d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko lastProgramEndTime < programList.get(programList.size() - 1).getEndTimeUtcMillis()); 215816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 216816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 217816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 218816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Test for {@link ProgramDataManager#setPauseProgramUpdate(boolean)}. 219816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 220816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void testSetPauseProgramUpdate() throws Exception { 221816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko final long testChannelId = 1; 222816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko startAndWaitForComplete(); 223816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Force program data manager to update program data whenever it's changes. 224816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.setProgramPrefetchUpdateWait(0); 225816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mListener.reset(); 226816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.setPauseProgramUpdate(true); 227816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentProvider.simulateAppend(testChannelId); 228816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertFalse(mListener.programUpdatedLatch.await(FAILURE_TIME_OUT_MS, 229816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TimeUnit.MILLISECONDS)); 230816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 231816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 2327d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko public static void assertProgramEquals(long expectedStartTime, ProgramInfo expectedInfo, 2337d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko Program actualProgram) { 2347d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertEquals("title", expectedInfo.title, actualProgram.getTitle()); 2357d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertEquals("episode", expectedInfo.episode, actualProgram.getEpisodeTitle()); 2367d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertEquals("description", expectedInfo.description, actualProgram.getDescription()); 2377d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertEquals("startTime", expectedStartTime, actualProgram.getStartTimeUtcMillis()); 2387d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko assertEquals("endTime", expectedStartTime + expectedInfo.durationMs, 2397d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko actualProgram.getEndTimeUtcMillis()); 2407d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko } 2417d67089aa1e9aa2123c3cd2f386d7019a1544db1Nick Chalko 242816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class FakeContentResolver extends MockContentResolver { 243816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 244816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) { 245816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko super.notifyChange(uri, observer, syncToNetwork); 246816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 247816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "onChanged(uri=" + uri + ")"); 248816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 249816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (observer != null) { 250816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko observer.dispatchChange(false, uri); 251816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 252816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramDataManager.getContentObserver().dispatchChange(false, uri); 253816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 254816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 255816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 256816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 257816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private static class ProgramInfoWrapper { 258816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final int index; 259816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final long startTimeMs; 260816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final ProgramInfo programInfo; 26107b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko 262816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public ProgramInfoWrapper(int index, long startTimeMs, ProgramInfo programInfo) { 263816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko this.index = index; 264816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko this.startTimeMs = startTimeMs; 265816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko this.programInfo = programInfo; 266816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 267816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 26807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko 269816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // This implements the minimal methods in content resolver 270816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // and detailed assumptions are written in each method. 271816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class FakeContentProvider extends MockContentProvider { 27207b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko private final SparseArray<List<ProgramInfoWrapper>> mProgramInfoList = new SparseArray<>(); 273816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 274816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 275816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Constructor for FakeContentProvider 276816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * <p> 277816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * This initializes program info assuming that 278816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * channel IDs are 1, 2, 3, ... {@link Constants#UNIT_TEST_CHANNEL_COUNT}. 279816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * </p> 280816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 281816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public FakeContentProvider(Context context) { 282816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko super(context); 283816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startTimeMs = Utils.floorTime( 284816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mClock.currentTimeMillis() - ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS, 285816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramDataManager.PROGRAM_GUIDE_SNAP_TIME_MS); 286816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long endTimeMs = startTimeMs + (ProgramDataManager.PROGRAM_GUIDE_MAX_TIME_RANGE / 2); 287816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) { 288816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<ProgramInfoWrapper> programInfoList = new ArrayList<>(); 289816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo stub = ProgramInfo.create(); 290816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int index = stub.getIndex(startTimeMs, i); 291816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long programStartTimeMs = stub.getStartTimeMs(index, i); 292816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (programStartTimeMs < endTimeMs) { 293816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo programAt = stub.build(getContext(), index); 294816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programInfoList.add( 295816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko new ProgramInfoWrapper(index, programStartTimeMs, programAt)); 296816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko index++; 297816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programStartTimeMs += programAt.durationMs; 298816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 299816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramInfoList.put(i, programInfoList); 300816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 301816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 302816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 303816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 304816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public Cursor query(Uri uri, String[] projection, String selection, 305816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String[] selectionArgs, String sortOrder) { 306816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 307816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "dump query"); 308816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, " uri=" + uri); 30907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko Log.d(TAG, " projection=" + Arrays.toString(projection)); 31007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko Log.d(TAG, " selection=" + selection); 311816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 312816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long startTimeMs = Long.parseLong(uri.getQueryParameter(PARAM_START_TIME)); 313816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long endTimeMs = Long.parseLong(uri.getQueryParameter(PARAM_END_TIME)); 314816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (startTimeMs == 0 || endTimeMs == 0) { 315816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko throw new UnsupportedOperationException(); 316816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 317816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertProgramUri(uri); 318816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long channelId; 319816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko try { 320816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelId = Long.parseLong(uri.getQueryParameter(PARAM_CHANNEL)); 321816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } catch (NumberFormatException e) { 322816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko channelId = -1; 323816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 324816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return new FakeCursor(projection, channelId, startTimeMs, endTimeMs); 325816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 326816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 327816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 328816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Simulate program data appends at the end of the existing programs. 329816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * This appends programs until the maximum program query range 330816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * ({@link ProgramDataManager#PROGRAM_GUIDE_MAX_TIME_RANGE}) 331816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * where we started with the inserting half of it. 332816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 333816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void simulateAppend(long channelId) { 334816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko long endTimeMs = 335816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mClock.currentTimeMillis() + ProgramDataManager.PROGRAM_GUIDE_MAX_TIME_RANGE; 336816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<ProgramInfoWrapper> programList = mProgramInfoList.get((int) channelId); 337816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mProgramInfoList == null) { 338816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return; 339816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 340816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo stub = ProgramInfo.create(); 341816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfoWrapper last = programList.get(programList.size() - 1); 342816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (last.startTimeMs < endTimeMs) { 343816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfo nextProgramInfo = stub.build(getContext(), last.index + 1); 344816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfoWrapper next = new ProgramInfoWrapper(last.index + 1, 345816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko last.startTimeMs + last.programInfo.durationMs, nextProgramInfo); 346816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programList.add(next); 347816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko last = next; 348816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 349816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mContentResolver.notifyChange(TvContract.Programs.CONTENT_URI, null); 350816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 351816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 352816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private void assertProgramUri(Uri uri) { 353816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko assertTrue("Uri(" + uri + ") isn't channel uri", 354816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko uri.toString().startsWith(TvContract.Programs.CONTENT_URI.toString())); 355816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 356816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 357816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public ProgramInfoWrapper get(long channelId, int position) { 358816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko List<ProgramInfoWrapper> programList = mProgramInfoList.get((int) channelId); 359816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (programList == null || position >= programList.size()) { 360816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return null; 361816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 362816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return programList.get(position); 363816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 364816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 365816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 366816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class FakeCursor extends MockCursor { 36707b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko private final String[] ALL_COLUMNS = { 368816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TvContract.Programs.COLUMN_CHANNEL_ID, 369816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TvContract.Programs.COLUMN_TITLE, 370816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TvContract.Programs.COLUMN_SHORT_DESCRIPTION, 371816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TvContract.Programs.COLUMN_EPISODE_TITLE, 372816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, 373816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS}; 374816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final String[] mColumns; 375816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final boolean mIsQueryForSingleChannel; 376816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final long mStartTimeMs; 377816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final long mEndTimeMs; 378816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private final int mCount; 379816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private long mChannelId; 380816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private int mProgramPosition; 381816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private ProgramInfoWrapper mCurrentProgram; 382816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 383816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko /** 384816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Constructor 385816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @param columns the same as projection passed from {@link FakeContentProvider#query}. 386816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Can be null for query all. 387816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @param channelId channel ID to query programs belongs to the specified channel. 388816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * Can be negative to indicate all channels. 389816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @param startTimeMs start of the time range to query programs. 390816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko * @param endTimeMs end of the time range to query programs. 391816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko */ 392816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public FakeCursor(String[] columns, long channelId, long startTimeMs, long endTimeMs) { 393816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mColumns = (columns == null) ? ALL_COLUMNS : columns; 394816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mIsQueryForSingleChannel = (channelId > 0); 395816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelId = channelId; 396816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramPosition = -1; 397816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mStartTimeMs = startTimeMs; 398816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mEndTimeMs = endTimeMs; 399816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko int count = 0; 400816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (moveToNext()) { 401816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko count++; 402816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 403816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCount = count; 404816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // Rewind channel Id and program index. 405816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mChannelId = channelId; 406816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramPosition = -1; 407816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 40807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko Log.d(TAG, "FakeCursor(columns=" + Arrays.toString(columns) 40907b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko + ", channelId=" + channelId + ", startTimeMs=" + startTimeMs 41007b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko + ", endTimeMs=" + endTimeMs + ") has mCount=" + mCount); 411816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 412816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 413816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 414816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 415816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public String getColumnName(int columnIndex) { 416816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mColumns[columnIndex]; 417816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 418816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 419816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 420816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public int getColumnIndex(String columnName) { 421816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko for (int i = 0; i < mColumns.length; i++) { 422816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mColumns[i].equalsIgnoreCase(columnName)) { 423816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return i; 424816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 425816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 426816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return -1; 427816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 428816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 429816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 430816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public int getInt(int columnIndex) { 431816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 432816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "Column (" + getColumnName(columnIndex) + ") is ignored in getInt()"); 433816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 434816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return 0; 435816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 436816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 437816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 438816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public long getLong(int columnIndex) { 439816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String columnName = getColumnName(columnIndex); 440816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko switch (columnName) { 441816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case TvContract.Programs.COLUMN_CHANNEL_ID: 442816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mChannelId; 443816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS: 444816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mCurrentProgram.startTimeMs; 445816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS: 446816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mCurrentProgram.startTimeMs + mCurrentProgram.programInfo.durationMs; 447816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 448816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 449816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "Column (" + columnName + ") is ignored in getLong()"); 450816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 451816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return 0; 452816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 453816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 454816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 455816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public String getString(int columnIndex) { 456816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko String columnName = getColumnName(columnIndex); 457816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko switch (columnName) { 458816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case TvContract.Programs.COLUMN_TITLE: 459816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mCurrentProgram.programInfo.title; 460816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case TvContract.Programs.COLUMN_SHORT_DESCRIPTION: 461816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mCurrentProgram.programInfo.description; 462816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko case TvContract.Programs.COLUMN_EPISODE_TITLE: 463816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mCurrentProgram.programInfo.episode; 464816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 465816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (DEBUG) { 466816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko Log.d(TAG, "Column (" + columnName + ") is ignored in getString()"); 467816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 468816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return null; 469816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 470816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 471816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 472816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public int getCount() { 473816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return mCount; 474816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 475816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 476816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 477816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public boolean moveToNext() { 478816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko while (true) { 479816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko ProgramInfoWrapper program = mContentProvider.get(mChannelId, ++mProgramPosition); 480816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (program == null || program.startTimeMs >= mEndTimeMs) { 481816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (mIsQueryForSingleChannel) { 482816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return false; 483816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else { 484816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko if (++mChannelId > Constants.UNIT_TEST_CHANNEL_COUNT) { 485816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return false; 486816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 487816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mProgramPosition = -1; 488816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 489816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } else if (program.startTimeMs + program.programInfo.durationMs >= mStartTimeMs) { 490816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko mCurrentProgram = program; 491816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko break; 492816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 493816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 494816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko return true; 495816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 496816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 497816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 498816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void close() { 499816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko // No-op. 500816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 501816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 502816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 503816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class TestProgramDataManagerListener implements ProgramDataManager.Listener { 504816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public CountDownLatch programUpdatedLatch = new CountDownLatch(1); 505816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 506816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 507816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void onProgramUpdated() { 508816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programUpdatedLatch.countDown(); 509816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 510816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 511816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void reset() { 512816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko programUpdatedLatch = new CountDownLatch(1); 513816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 514816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 515816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 516816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko private class TestProgramDataManagerOnCurrentProgramUpdatedListener implements 517816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko OnCurrentProgramUpdatedListener { 51807b043dc3db83d6d20f0e8513b946830ab00e37bNick Chalko public final CountDownLatch currentProgramUpdatedLatch = new CountDownLatch(1); 519816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public long updatedChannelId = -1; 520816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public Program updatedProgram = null; 521816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko 522816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko @Override 523816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko public void onCurrentProgramUpdated(long channelId, Program program) { 524816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updatedChannelId = channelId; 525816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko updatedProgram = program; 526816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko currentProgramUpdatedLatch.countDown(); 527816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 528816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko } 529816a4be1a0f34f6a48877c8afd3dbbca19eac435Nick Chalko} 530