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