Home | History | Annotate | Download | only in data
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.tv.data;
     18 
     19 import android.content.ContentProvider;
     20 import android.content.ContentUris;
     21 import android.content.ContentValues;
     22 import android.content.Context;
     23 import android.database.ContentObserver;
     24 import android.database.Cursor;
     25 import android.media.tv.TvContract;
     26 import android.media.tv.TvContract.Channels;
     27 import android.net.Uri;
     28 import android.test.AndroidTestCase;
     29 import android.test.MoreAsserts;
     30 import android.test.UiThreadTest;
     31 import android.test.mock.MockContentProvider;
     32 import android.test.mock.MockContentResolver;
     33 import android.test.mock.MockCursor;
     34 import android.test.suitebuilder.annotation.SmallTest;
     35 import android.text.TextUtils;
     36 import android.util.Log;
     37 import android.util.SparseArray;
     38 
     39 import com.android.tv.testing.ChannelInfo;
     40 import com.android.tv.testing.Constants;
     41 import com.android.tv.testing.Utils;
     42 import com.android.tv.util.TvInputManagerHelper;
     43 
     44 import org.mockito.Matchers;
     45 import org.mockito.Mockito;
     46 
     47 import java.util.ArrayList;
     48 import java.util.Arrays;
     49 import java.util.List;
     50 import java.util.concurrent.CountDownLatch;
     51 import java.util.concurrent.TimeUnit;
     52 
     53 /**
     54  * Test for {@link ChannelDataManager}
     55  *
     56  * A test method may include tests for multiple methods to minimize the DB access.
     57  * Note that all the methods of {@link ChannelDataManager} should be called from the UI thread.
     58  */
     59 @SmallTest
     60 public class ChannelDataManagerTest extends AndroidTestCase {
     61     private static final boolean DEBUG = false;
     62     private static final String TAG = "ChannelDataManagerTest";
     63 
     64     // Wait time for expected success.
     65     private static final long WAIT_TIME_OUT_MS = 1000L;
     66     private static final String DUMMY_INPUT_ID = "dummy";
     67     // TODO: Use Channels.COLUMN_BROWSABLE and Channels.COLUMN_LOCKED instead.
     68     private static final String COLUMN_BROWSABLE = "browsable";
     69     private static final String COLUMN_LOCKED = "locked";
     70 
     71     private ChannelDataManager mChannelDataManager;
     72     private TestChannelDataManagerListener mListener;
     73     private FakeContentResolver mContentResolver;
     74     private FakeContentProvider mContentProvider;
     75 
     76     @Override
     77     protected void setUp() throws Exception {
     78         super.setUp();
     79         assertTrue("More than 2 channels to test", Constants.UNIT_TEST_CHANNEL_COUNT > 2);
     80 
     81         mContentProvider = new FakeContentProvider(getContext());
     82         mContentResolver = new FakeContentResolver();
     83         mContentResolver.addProvider(TvContract.AUTHORITY, mContentProvider);
     84         mListener = new TestChannelDataManagerListener();
     85         Utils.runOnMainSync(new Runnable() {
     86             @Override
     87             public void run() {
     88                 TvInputManagerHelper mockHelper = Mockito.mock(TvInputManagerHelper.class);
     89                 Mockito.when(mockHelper.hasTvInputInfo(Matchers.anyString())).thenReturn(true);
     90                 mChannelDataManager = new ChannelDataManager(getContext(), mockHelper,
     91                         mContentResolver);
     92                 mChannelDataManager.addListener(mListener);
     93             }
     94         });
     95     }
     96 
     97     @Override
     98     protected void tearDown() throws Exception {
     99         Utils.runOnMainSync(new Runnable() {
    100             @Override
    101             public void run() {
    102                 mChannelDataManager.stop();
    103             }
    104         });
    105         super.tearDown();
    106     }
    107 
    108     private void startAndWaitForComplete() throws Exception {
    109         mChannelDataManager.start();
    110         try {
    111             assertTrue(mListener.loadFinishedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
    112         } catch (InterruptedException e) {
    113             throw e;
    114         }
    115     }
    116 
    117     private void restart() throws Exception {
    118         mChannelDataManager.stop();
    119         mListener.reset();
    120         startAndWaitForComplete();
    121     }
    122 
    123     @UiThreadTest
    124     public void testIsDbLoadFinished() throws Exception {
    125         startAndWaitForComplete();
    126         assertTrue(mChannelDataManager.isDbLoadFinished());
    127     }
    128 
    129     /**
    130      * Test for following methods
    131      *   - {@link ChannelDataManager#getChannelCount}
    132      *   - {@link ChannelDataManager#getChannelList}
    133      *   - {@link ChannelDataManager#getChannel}
    134      */
    135     @UiThreadTest
    136     public void testGetChannels() throws Exception {
    137         startAndWaitForComplete();
    138 
    139         // Test {@link ChannelDataManager#getChannelCount}
    140         assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT, mChannelDataManager.getChannelCount());
    141 
    142         // Test {@link ChannelDataManager#getChannelList}
    143         List<ChannelInfo> channelInfoList = new ArrayList<>();
    144         for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) {
    145             channelInfoList.add(ChannelInfo.create(getContext(), i));
    146         }
    147         List<Channel> channelList = mChannelDataManager.getChannelList();
    148         for (Channel channel : channelList) {
    149             boolean found = false;
    150             for (ChannelInfo channelInfo : channelInfoList) {
    151                 if (TextUtils.equals(channelInfo.name, channel.getDisplayName())
    152                         && TextUtils.equals(channelInfo.name, channel.getDisplayName())) {
    153                     found = true;
    154                     channelInfoList.remove(channelInfo);
    155                     break;
    156                 }
    157             }
    158             assertTrue("Cannot find (" + channel + ")", found);
    159         }
    160 
    161         // Test {@link ChannelDataManager#getChannelIndex()}
    162         for (Channel channel : channelList) {
    163             assertEquals(channel, mChannelDataManager.getChannel(channel.getId()));
    164         }
    165     }
    166 
    167     /**
    168      * Test for {@link ChannelDataManager#getChannelCount} when no channel is available.
    169      */
    170     @UiThreadTest
    171     public void testGetChannels_noChannels() throws Exception {
    172         mContentProvider.clear();
    173         startAndWaitForComplete();
    174         assertEquals(0, mChannelDataManager.getChannelCount());
    175     }
    176 
    177     /**
    178      * Test for following methods and channel listener with notifying change.
    179      *   - {@link ChannelDataManager#updateBrowsable}
    180      *   - {@link ChannelDataManager#applyUpdatedValuesToDb}
    181      */
    182     @UiThreadTest
    183     public void testBrowsable() throws Exception {
    184         startAndWaitForComplete();
    185 
    186         // Test if all channels are browsable
    187         List<Channel> channelList = new ArrayList<>(mChannelDataManager.getChannelList());
    188         List<Channel> browsableChannelList = mChannelDataManager.getBrowsableChannelList();
    189         for (Channel browsableChannel : browsableChannelList) {
    190             boolean found = channelList.remove(browsableChannel);
    191             assertTrue("Cannot find (" + browsableChannel + ")", found);
    192         }
    193         assertEquals(0, channelList.size());
    194 
    195         // Prepare for next tests.
    196         TestChannelDataManagerChannelListener channelListener =
    197                 new TestChannelDataManagerChannelListener();
    198         Channel channel1 = mChannelDataManager.getChannelList().get(0);
    199         mChannelDataManager.addChannelListener(channel1.getId(), channelListener);
    200 
    201         // Test {@link ChannelDataManager#updateBrowsable} & notification.
    202         mChannelDataManager.updateBrowsable(channel1.getId(), false, false);
    203         assertTrue(mListener.channelBrowsableChangedCalled);
    204         assertFalse(mChannelDataManager.getBrowsableChannelList().contains(channel1));
    205         MoreAsserts.assertContentsInAnyOrder(channelListener.updatedChannels, channel1);
    206         channelListener.reset();
    207 
    208         // Test {@link ChannelDataManager#applyUpdatedValuesToDb}
    209         // Disable the update notification to avoid the unwanted call of "onLoadFinished".
    210         mContentResolver.mNotifyDisabled = true;
    211         mChannelDataManager.applyUpdatedValuesToDb();
    212         restart();
    213         browsableChannelList = mChannelDataManager.getBrowsableChannelList();
    214         assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT - 1, browsableChannelList.size());
    215         assertFalse(browsableChannelList.contains(channel1));
    216     }
    217 
    218     /**
    219      * Test for following methods and channel listener without notifying change.
    220      *   - {@link ChannelDataManager#updateBrowsable}
    221      *   - {@link ChannelDataManager#applyUpdatedValuesToDb}
    222      */
    223     @UiThreadTest
    224     public void testBrowsable_skipNotification() throws Exception {
    225         startAndWaitForComplete();
    226 
    227         // Prepare for next tests.
    228         TestChannelDataManagerChannelListener channelListener =
    229                 new TestChannelDataManagerChannelListener();
    230         Channel channel1 = mChannelDataManager.getChannelList().get(0);
    231         Channel channel2 = mChannelDataManager.getChannelList().get(1);
    232         mChannelDataManager.addChannelListener(channel1.getId(), channelListener);
    233         mChannelDataManager.addChannelListener(channel2.getId(), channelListener);
    234 
    235         // Test {@link ChannelDataManager#updateBrowsable} & skip notification.
    236         mChannelDataManager.updateBrowsable(channel1.getId(), false, true);
    237         mChannelDataManager.updateBrowsable(channel2.getId(), false, true);
    238         mChannelDataManager.updateBrowsable(channel1.getId(), true, true);
    239         assertFalse(mListener.channelBrowsableChangedCalled);
    240         List<Channel> browsableChannelList = mChannelDataManager.getBrowsableChannelList();
    241         assertTrue(browsableChannelList.contains(channel1));
    242         assertFalse(browsableChannelList.contains(channel2));
    243 
    244         // Test {@link ChannelDataManager#applyUpdatedValuesToDb}
    245         // Disable the update notification to avoid the unwanted call of "onLoadFinished".
    246         mContentResolver.mNotifyDisabled = true;
    247         mChannelDataManager.applyUpdatedValuesToDb();
    248         restart();
    249         browsableChannelList = mChannelDataManager.getBrowsableChannelList();
    250         assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT - 1, browsableChannelList.size());
    251         assertFalse(browsableChannelList.contains(channel2));
    252     }
    253 
    254     /**
    255      * Test for following methods and channel listener.
    256      *   - {@link ChannelDataManager#updateLocked}
    257      *   - {@link ChannelDataManager#applyUpdatedValuesToDb}
    258      */
    259     @UiThreadTest
    260     public void testLocked() throws Exception {
    261         startAndWaitForComplete();
    262 
    263         // Test if all channels aren't locked at the first time.
    264         List<Channel> channelList = mChannelDataManager.getChannelList();
    265         for (Channel channel : channelList) {
    266             assertFalse(channel + " is locked", channel.isLocked());
    267         }
    268 
    269         // Prepare for next tests.
    270         Channel channel = mChannelDataManager.getChannelList().get(0);
    271 
    272         // Test {@link ChannelDataManager#updateLocked}
    273         mChannelDataManager.updateLocked(channel.getId(), true);
    274         assertTrue(mChannelDataManager.getChannel(channel.getId()).isLocked());
    275 
    276         // Test {@link ChannelDataManager#applyUpdatedValuesToDb}.
    277         // Disable the update notification to avoid the unwanted call of "onLoadFinished".
    278         mContentResolver.mNotifyDisabled = true;
    279         mChannelDataManager.applyUpdatedValuesToDb();
    280         restart();
    281         assertTrue(mChannelDataManager.getChannel(channel.getId()).isLocked());
    282 
    283         // Cleanup
    284         mChannelDataManager.updateLocked(channel.getId(), false);
    285     }
    286 
    287     /**
    288      * Test ChannelDataManager when channels in TvContract are updated, removed, or added.
    289      */
    290     @UiThreadTest
    291     public void testChannelListChanged() throws Exception {
    292         startAndWaitForComplete();
    293 
    294         // Test channel add.
    295         mListener.reset();
    296         long testChannelId = Constants.UNIT_TEST_CHANNEL_COUNT + 1;
    297         ChannelInfo testChannelInfo = ChannelInfo.create(getContext(), (int) testChannelId);
    298         testChannelId = Constants.UNIT_TEST_CHANNEL_COUNT + 1;
    299         mContentProvider.simulateInsert(testChannelInfo);
    300         assertTrue(
    301                 mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
    302         assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT + 1, mChannelDataManager.getChannelCount());
    303 
    304         // Test channel update
    305         mListener.reset();
    306         TestChannelDataManagerChannelListener channelListener =
    307                 new TestChannelDataManagerChannelListener();
    308         mChannelDataManager.addChannelListener(testChannelId, channelListener);
    309         String newName = testChannelInfo.name + "_test";
    310         mContentProvider.simulateUpdate(testChannelId, newName);
    311         assertTrue(
    312                 mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
    313         assertTrue(
    314                 channelListener.channelChangedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
    315         assertEquals(0, channelListener.removedChannels.size());
    316         assertEquals(1, channelListener.updatedChannels.size());
    317         Channel updatedChannel = channelListener.updatedChannels.get(0);
    318         assertEquals(testChannelId, updatedChannel.getId());
    319         assertEquals(testChannelInfo.number, updatedChannel.getDisplayNumber());
    320         assertEquals(newName, updatedChannel.getDisplayName());
    321         assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT + 1,
    322                 mChannelDataManager.getChannelCount());
    323 
    324         // Test channel remove.
    325         mListener.reset();
    326         channelListener.reset();
    327         mContentProvider.simulateDelete(testChannelId);
    328         assertTrue(
    329                 mListener.channelListUpdatedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
    330         assertTrue(
    331                 channelListener.channelChangedLatch.await(WAIT_TIME_OUT_MS, TimeUnit.MILLISECONDS));
    332         assertEquals(1, channelListener.removedChannels.size());
    333         assertEquals(0, channelListener.updatedChannels.size());
    334         Channel removedChannel = channelListener.removedChannels.get(0);
    335         assertEquals(newName, removedChannel.getDisplayName());
    336         assertEquals(testChannelInfo.number, removedChannel.getDisplayNumber());
    337         assertEquals(Constants.UNIT_TEST_CHANNEL_COUNT, mChannelDataManager.getChannelCount());
    338     }
    339 
    340     private class ChannelInfoWrapper {
    341         public ChannelInfo channelInfo;
    342         public boolean browsable;
    343         public boolean locked;
    344         public ChannelInfoWrapper(ChannelInfo channelInfo) {
    345             this.channelInfo = channelInfo;
    346             browsable = true;
    347             locked = false;
    348         }
    349     }
    350 
    351     private class FakeContentResolver extends MockContentResolver {
    352         boolean mNotifyDisabled;
    353 
    354         @Override
    355         public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
    356             super.notifyChange(uri, observer, syncToNetwork);
    357             if (DEBUG) {
    358                 Log.d(TAG, "onChanged(uri=" + uri + ", observer=" + observer + ") - Notification "
    359                         + (mNotifyDisabled ? "disabled" : "enabled"));
    360             }
    361             if (mNotifyDisabled) {
    362                 return;
    363             }
    364             // Do not call {@link ContentObserver#onChange} directly to run it on the correct
    365             // thread.
    366             if (observer != null) {
    367                 observer.dispatchChange(false, uri);
    368             } else {
    369                 mChannelDataManager.getContentObserver().dispatchChange(false, uri);
    370             }
    371         }
    372     }
    373 
    374     // This implements the minimal methods in content resolver
    375     // and detailed assumptions are written in each method.
    376     private class FakeContentProvider extends MockContentProvider {
    377         private final SparseArray<ChannelInfoWrapper> mChannelInfoList = new SparseArray<>();
    378 
    379         public FakeContentProvider(Context context) {
    380             super(context);
    381             for (int i = 1; i <= Constants.UNIT_TEST_CHANNEL_COUNT; i++) {
    382                 mChannelInfoList.put(i,
    383                         new ChannelInfoWrapper(ChannelInfo.create(getContext(), i)));
    384             }
    385         }
    386 
    387         /**
    388          * Implementation of {@link ContentProvider#query}.
    389          * This assumes that {@link ChannelDataManager} queries channels
    390          * with empty {@code selection}. (i.e. channels are always queries for all)
    391          */
    392         @Override
    393         public Cursor query(Uri uri, String[] projection, String selection, String[]
    394                 selectionArgs, String sortOrder) {
    395             if (DEBUG) {
    396                 Log.d(TAG, "dump query");
    397                 Log.d(TAG, "  uri=" + uri);
    398                 Log.d(TAG, "  projection=" + Arrays.toString(projection));
    399                 Log.d(TAG, "  selection=" + selection);
    400             }
    401             assertChannelUri(uri);
    402             return new FakeCursor(projection);
    403         }
    404 
    405         /**
    406          * Implementation of {@link ContentProvider#update}.
    407          * This assumes that {@link ChannelDataManager} update channels
    408          * only for changing browsable and locked.
    409          */
    410         @Override
    411         public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    412             if (DEBUG) Log.d(TAG, "update(uri=" + uri + ", selection=" + selection);
    413             assertChannelUri(uri);
    414             List<Long> channelIds = new ArrayList<>();
    415             try {
    416                 long channelId = ContentUris.parseId(uri);
    417                 channelIds.add(channelId);
    418             } catch (NumberFormatException e) {
    419                 // Update for multiple channels.
    420                 if (TextUtils.isEmpty(selection)) {
    421                     for (int i = 0; i < mChannelInfoList.size(); i++) {
    422                         channelIds.add((long) mChannelInfoList.keyAt(i));
    423                     }
    424                 } else {
    425                     // See {@link Utils#buildSelectionForIds} for the syntax.
    426                     String selectionForId = selection.substring(
    427                             selection.indexOf("(") + 1, selection.lastIndexOf(")"));
    428                     String[] ids = selectionForId.split(", ");
    429                     if (ids != null) {
    430                         for (String id : ids) {
    431                             channelIds.add(Long.parseLong(id));
    432                         }
    433                     }
    434                 }
    435             }
    436             int updateCount = 0;
    437             for (long channelId : channelIds) {
    438                 boolean updated = false;
    439                 ChannelInfoWrapper channel = mChannelInfoList.get((int) channelId);
    440                 if (channel == null) {
    441                     return 0;
    442                 }
    443                 if (values.containsKey(COLUMN_BROWSABLE)) {
    444                     updated = true;
    445                     channel.browsable = (values.getAsInteger(COLUMN_BROWSABLE) == 1);
    446                 }
    447                 if (values.containsKey(COLUMN_LOCKED)) {
    448                     updated = true;
    449                     channel.locked = (values.getAsInteger(COLUMN_LOCKED) == 1);
    450                 }
    451                 updateCount += updated ? 1 : 0;
    452             }
    453             if (updateCount > 0) {
    454                 if (channelIds.size() == 1) {
    455                     mContentResolver.notifyChange(uri, null);
    456                 } else {
    457                     mContentResolver.notifyChange(Channels.CONTENT_URI, null);
    458                 }
    459             } else {
    460                 if (DEBUG) {
    461                     Log.d(TAG, "Update to channel(uri=" + uri + ") is ignored for " + values);
    462                 }
    463             }
    464             return updateCount;
    465         }
    466 
    467         /**
    468          * Simulates channel data insert.
    469          * This assigns original network ID (the same with channel number) to channel ID.
    470          */
    471         public void simulateInsert(ChannelInfo testChannelInfo) {
    472             long channelId = testChannelInfo.originalNetworkId;
    473             mChannelInfoList.put((int) channelId,
    474                     new ChannelInfoWrapper(ChannelInfo.create(getContext(), (int) channelId)));
    475             mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null);
    476         }
    477 
    478         /**
    479          * Simulates channel data delete.
    480          */
    481         public void simulateDelete(long channelId) {
    482             mChannelInfoList.remove((int) channelId);
    483             mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null);
    484         }
    485 
    486         /**
    487          * Simulates channel data update.
    488          */
    489         public void simulateUpdate(long channelId, String newName) {
    490             ChannelInfoWrapper channel = mChannelInfoList.get((int) channelId);
    491             ChannelInfo.Builder builder = new ChannelInfo.Builder(channel.channelInfo);
    492             builder.setName(newName);
    493             channel.channelInfo = builder.build();
    494             mContentResolver.notifyChange(TvContract.buildChannelUri(channelId), null);
    495         }
    496 
    497         private void assertChannelUri(Uri uri) {
    498             assertTrue("Uri(" + uri + ") isn't channel uri",
    499                     uri.toString().startsWith(Channels.CONTENT_URI.toString()));
    500         }
    501 
    502         public void clear() {
    503             mChannelInfoList.clear();
    504         }
    505 
    506         public ChannelInfoWrapper get(int position) {
    507             return mChannelInfoList.get(mChannelInfoList.keyAt(position));
    508         }
    509 
    510         public int getCount() {
    511             return mChannelInfoList.size();
    512         }
    513 
    514         public long keyAt(int position) {
    515             return mChannelInfoList.keyAt(position);
    516         }
    517     }
    518 
    519     private class FakeCursor extends MockCursor {
    520         private final String[] ALL_COLUMNS =  {
    521                 Channels._ID,
    522                 Channels.COLUMN_DISPLAY_NAME,
    523                 Channels.COLUMN_DISPLAY_NUMBER,
    524                 Channels.COLUMN_INPUT_ID,
    525                 Channels.COLUMN_VIDEO_FORMAT,
    526                 Channels.COLUMN_ORIGINAL_NETWORK_ID,
    527                 COLUMN_BROWSABLE,
    528                 COLUMN_LOCKED};
    529         private final String[] mColumns;
    530         private int mPosition;
    531 
    532         public FakeCursor(String[] columns) {
    533             mColumns = (columns == null) ? ALL_COLUMNS : columns;
    534             mPosition = -1;
    535         }
    536 
    537         @Override
    538         public String getColumnName(int columnIndex) {
    539             return mColumns[columnIndex];
    540         }
    541 
    542         @Override
    543         public int getColumnIndex(String columnName) {
    544             for (int i = 0; i < mColumns.length; i++) {
    545                 if (mColumns[i].equalsIgnoreCase(columnName)) {
    546                     return i;
    547                 }
    548             }
    549             return -1;
    550         }
    551 
    552         @Override
    553         public long getLong(int columnIndex) {
    554             String columnName = getColumnName(columnIndex);
    555             switch (columnName) {
    556                 case Channels._ID:
    557                     return mContentProvider.keyAt(mPosition);
    558             }
    559             if (DEBUG) {
    560                 Log.d(TAG, "Column (" + columnName + ") is ignored in getLong()");
    561             }
    562             return 0;
    563         }
    564 
    565         @Override
    566         public String getString(int columnIndex) {
    567             String columnName = getColumnName(columnIndex);
    568             ChannelInfoWrapper channel = mContentProvider.get(mPosition);
    569             switch (columnName) {
    570                 case Channels.COLUMN_DISPLAY_NAME:
    571                     return channel.channelInfo.name;
    572                 case Channels.COLUMN_DISPLAY_NUMBER:
    573                     return channel.channelInfo.number;
    574                 case Channels.COLUMN_INPUT_ID:
    575                     return DUMMY_INPUT_ID;
    576                 case Channels.COLUMN_VIDEO_FORMAT:
    577                     return channel.channelInfo.getVideoFormat();
    578             }
    579             if (DEBUG) {
    580                 Log.d(TAG, "Column (" + columnName + ") is ignored in getString()");
    581             }
    582             return null;
    583         }
    584 
    585         @Override
    586         public int getInt(int columnIndex) {
    587             String columnName = getColumnName(columnIndex);
    588             ChannelInfoWrapper channel = mContentProvider.get(mPosition);
    589             switch (columnName) {
    590                 case Channels.COLUMN_ORIGINAL_NETWORK_ID:
    591                     return channel.channelInfo.originalNetworkId;
    592                 case COLUMN_BROWSABLE:
    593                     return channel.browsable ? 1 : 0;
    594                 case COLUMN_LOCKED:
    595                     return channel.locked ? 1 : 0;
    596             }
    597             if (DEBUG) {
    598                 Log.d(TAG, "Column (" + columnName + ") is ignored in getInt()");
    599             }
    600             return 0;
    601         }
    602 
    603         @Override
    604         public int getCount() {
    605             return mContentProvider.getCount();
    606         }
    607 
    608         @Override
    609         public boolean moveToNext() {
    610             return ++mPosition < mContentProvider.getCount();
    611         }
    612 
    613         @Override
    614         public void close() {
    615             // No-op.
    616         }
    617     }
    618 
    619     private class TestChannelDataManagerListener implements ChannelDataManager.Listener {
    620         public CountDownLatch loadFinishedLatch = new CountDownLatch(1);
    621         public CountDownLatch channelListUpdatedLatch = new CountDownLatch(1);
    622         public boolean channelBrowsableChangedCalled;
    623 
    624         @Override
    625         public void onLoadFinished() {
    626             loadFinishedLatch.countDown();
    627         }
    628 
    629         @Override
    630         public void onChannelListUpdated() {
    631             channelListUpdatedLatch.countDown();
    632         }
    633 
    634         @Override
    635         public void onChannelBrowsableChanged() {
    636             channelBrowsableChangedCalled = true;
    637         }
    638 
    639         public void reset() {
    640             loadFinishedLatch = new CountDownLatch(1);
    641             channelListUpdatedLatch = new CountDownLatch(1);
    642             channelBrowsableChangedCalled = false;
    643         }
    644     }
    645 
    646     private class TestChannelDataManagerChannelListener
    647             implements ChannelDataManager.ChannelListener {
    648         public CountDownLatch channelChangedLatch = new CountDownLatch(1);
    649         public final List<Channel> removedChannels = new ArrayList<>();
    650         public final List<Channel> updatedChannels = new ArrayList<>();
    651 
    652         @Override
    653         public void onChannelRemoved(Channel channel) {
    654             removedChannels.add(channel);
    655             channelChangedLatch.countDown();
    656         }
    657 
    658         @Override
    659         public void onChannelUpdated(Channel channel) {
    660             updatedChannels.add(channel);
    661             channelChangedLatch.countDown();
    662         }
    663 
    664         public void reset() {
    665             channelChangedLatch = new CountDownLatch(1);
    666             removedChannels.clear();
    667             updatedChannels.clear();
    668         }
    669     }
    670 }
    671