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